| 1 |
// Written in the D programming language. |
|---|
| 2 |
|
|---|
| 3 |
/** |
|---|
| 4 |
Standard I/O functions that extend $(B std.c.stdio). $(B std.c.stdio) |
|---|
| 5 |
is $(D_PARAM public)ally imported when importing $(B std.stdio). |
|---|
| 6 |
|
|---|
| 7 |
Source: $(PHOBOSSRC std/_stdio.d) |
|---|
| 8 |
Macros: |
|---|
| 9 |
WIKI=Phobos/StdStdio |
|---|
| 10 |
|
|---|
| 11 |
Copyright: Copyright Digital Mars 2007-. |
|---|
| 12 |
License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). |
|---|
| 13 |
Authors: $(WEB digitalmars.com, Walter Bright), |
|---|
| 14 |
$(WEB erdani.org, Andrei Alexandrescu) |
|---|
| 15 |
*/ |
|---|
| 16 |
module std.stdio; |
|---|
| 17 |
|
|---|
| 18 |
public import core.stdc.stdio; |
|---|
| 19 |
import std.stdiobase; |
|---|
| 20 |
import core.memory, core.stdc.errno, core.stdc.stddef, core.stdc.stdlib, |
|---|
| 21 |
core.stdc.string, core.stdc.wchar_; |
|---|
| 22 |
import std.algorithm, std.array, std.conv, std.exception, std.file, std.format, |
|---|
| 23 |
std.range, std.string, std.traits, std.typecons, |
|---|
| 24 |
std.typetuple, std.utf; |
|---|
| 25 |
|
|---|
| 26 |
version (DigitalMars) version (Windows) |
|---|
| 27 |
{ |
|---|
| 28 |
// Specific to the way Digital Mars C does stdio |
|---|
| 29 |
version = DIGITAL_MARS_STDIO; |
|---|
| 30 |
import std.c.stdio : __fhnd_info, FHND_WCHAR, FHND_TEXT; |
|---|
| 31 |
} |
|---|
| 32 |
|
|---|
| 33 |
version (Posix) |
|---|
| 34 |
{ |
|---|
| 35 |
import core.sys.posix.stdio; |
|---|
| 36 |
alias core.sys.posix.stdio.fileno fileno; |
|---|
| 37 |
} |
|---|
| 38 |
|
|---|
| 39 |
version (linux) |
|---|
| 40 |
{ |
|---|
| 41 |
// Specific to the way Gnu C does stdio |
|---|
| 42 |
version = GCC_IO; |
|---|
| 43 |
extern(C) FILE* fopen64(const char*, const char*); |
|---|
| 44 |
} |
|---|
| 45 |
|
|---|
| 46 |
version (OSX) |
|---|
| 47 |
{ |
|---|
| 48 |
version = GENERIC_IO; |
|---|
| 49 |
alias core.stdc.stdio.fopen fopen64; |
|---|
| 50 |
} |
|---|
| 51 |
|
|---|
| 52 |
version (FreeBSD) |
|---|
| 53 |
{ |
|---|
| 54 |
version = GENERIC_IO; |
|---|
| 55 |
alias core.stdc.stdio.fopen fopen64; |
|---|
| 56 |
} |
|---|
| 57 |
|
|---|
| 58 |
version(Windows) |
|---|
| 59 |
{ |
|---|
| 60 |
alias core.stdc.stdio.fopen fopen64; |
|---|
| 61 |
} |
|---|
| 62 |
|
|---|
| 63 |
version (DIGITAL_MARS_STDIO) |
|---|
| 64 |
{ |
|---|
| 65 |
extern (C) |
|---|
| 66 |
{ |
|---|
| 67 |
/* ** |
|---|
| 68 |
* Digital Mars under-the-hood C I/O functions. |
|---|
| 69 |
* Use _iobuf* for the unshared version of FILE*, |
|---|
| 70 |
* usable when the FILE is locked. |
|---|
| 71 |
*/ |
|---|
| 72 |
int _fputc_nlock(int, _iobuf*); |
|---|
| 73 |
int _fputwc_nlock(int, _iobuf*); |
|---|
| 74 |
int _fgetc_nlock(_iobuf*); |
|---|
| 75 |
int _fgetwc_nlock(_iobuf*); |
|---|
| 76 |
int __fp_lock(FILE*); |
|---|
| 77 |
void __fp_unlock(FILE*); |
|---|
| 78 |
|
|---|
| 79 |
int setmode(int, int); |
|---|
| 80 |
} |
|---|
| 81 |
alias _fputc_nlock FPUTC; |
|---|
| 82 |
alias _fputwc_nlock FPUTWC; |
|---|
| 83 |
alias _fgetc_nlock FGETC; |
|---|
| 84 |
alias _fgetwc_nlock FGETWC; |
|---|
| 85 |
|
|---|
| 86 |
alias __fp_lock FLOCK; |
|---|
| 87 |
alias __fp_unlock FUNLOCK; |
|---|
| 88 |
|
|---|
| 89 |
alias setmode _setmode; |
|---|
| 90 |
enum _O_BINARY = 0x8000; |
|---|
| 91 |
int _fileno(FILE* f) { return f._file; } |
|---|
| 92 |
alias _fileno fileno; |
|---|
| 93 |
} |
|---|
| 94 |
else version (GCC_IO) |
|---|
| 95 |
{ |
|---|
| 96 |
/* ** |
|---|
| 97 |
* Gnu under-the-hood C I/O functions; see |
|---|
| 98 |
* http://gnu.org/software/libc/manual/html_node/I_002fO-on-Streams.html |
|---|
| 99 |
*/ |
|---|
| 100 |
extern (C) |
|---|
| 101 |
{ |
|---|
| 102 |
int fputc_unlocked(int, _iobuf*); |
|---|
| 103 |
int fputwc_unlocked(wchar_t, _iobuf*); |
|---|
| 104 |
int fgetc_unlocked(_iobuf*); |
|---|
| 105 |
int fgetwc_unlocked(_iobuf*); |
|---|
| 106 |
void flockfile(FILE*); |
|---|
| 107 |
void funlockfile(FILE*); |
|---|
| 108 |
ssize_t getline(char**, size_t*, FILE*); |
|---|
| 109 |
ssize_t getdelim (char**, size_t*, int, FILE*); |
|---|
| 110 |
|
|---|
| 111 |
private size_t fwrite_unlocked(const(void)* ptr, |
|---|
| 112 |
size_t size, size_t n, _iobuf *stream); |
|---|
| 113 |
} |
|---|
| 114 |
|
|---|
| 115 |
version (linux) |
|---|
| 116 |
{ |
|---|
| 117 |
// declare fopen64 if not already |
|---|
| 118 |
static if (!is(typeof(fopen64))) |
|---|
| 119 |
extern (C) FILE* fopen64(in char*, in char*); |
|---|
| 120 |
} |
|---|
| 121 |
|
|---|
| 122 |
alias fputc_unlocked FPUTC; |
|---|
| 123 |
alias fputwc_unlocked FPUTWC; |
|---|
| 124 |
alias fgetc_unlocked FGETC; |
|---|
| 125 |
alias fgetwc_unlocked FGETWC; |
|---|
| 126 |
|
|---|
| 127 |
alias flockfile FLOCK; |
|---|
| 128 |
alias funlockfile FUNLOCK; |
|---|
| 129 |
} |
|---|
| 130 |
else version (GENERIC_IO) |
|---|
| 131 |
{ |
|---|
| 132 |
extern (C) |
|---|
| 133 |
{ |
|---|
| 134 |
void flockfile(FILE*); |
|---|
| 135 |
void funlockfile(FILE*); |
|---|
| 136 |
} |
|---|
| 137 |
|
|---|
| 138 |
int fputc_unlocked(int c, _iobuf* fp) { return fputc(c, cast(shared) fp); } |
|---|
| 139 |
int fputwc_unlocked(wchar_t c, _iobuf* fp) |
|---|
| 140 |
{ |
|---|
| 141 |
return fputwc(c, cast(shared) fp); |
|---|
| 142 |
} |
|---|
| 143 |
int fgetc_unlocked(_iobuf* fp) { return fgetc(cast(shared) fp); } |
|---|
| 144 |
int fgetwc_unlocked(_iobuf* fp) { return fgetwc(cast(shared) fp); } |
|---|
| 145 |
|
|---|
| 146 |
alias fputc_unlocked FPUTC; |
|---|
| 147 |
alias fputwc_unlocked FPUTWC; |
|---|
| 148 |
alias fgetc_unlocked FGETC; |
|---|
| 149 |
alias fgetwc_unlocked FGETWC; |
|---|
| 150 |
|
|---|
| 151 |
alias flockfile FLOCK; |
|---|
| 152 |
alias funlockfile FUNLOCK; |
|---|
| 153 |
} |
|---|
| 154 |
else |
|---|
| 155 |
{ |
|---|
| 156 |
static assert(0, "unsupported C I/O system"); |
|---|
| 157 |
} |
|---|
| 158 |
|
|---|
| 159 |
//------------------------------------------------------------------------------ |
|---|
| 160 |
struct ByRecord(Fields...) |
|---|
| 161 |
{ |
|---|
| 162 |
private: |
|---|
| 163 |
File file; |
|---|
| 164 |
char[] line; |
|---|
| 165 |
Tuple!(Fields) current; |
|---|
| 166 |
string format; |
|---|
| 167 |
|
|---|
| 168 |
public: |
|---|
| 169 |
this(File f, string format) |
|---|
| 170 |
{ |
|---|
| 171 |
assert(f.isOpen); |
|---|
| 172 |
file = f; |
|---|
| 173 |
this.format = format; |
|---|
| 174 |
popFront; // prime the range |
|---|
| 175 |
} |
|---|
| 176 |
|
|---|
| 177 |
/// Range primitive implementations. |
|---|
| 178 |
@property bool empty() |
|---|
| 179 |
{ |
|---|
| 180 |
return !file.isOpen; |
|---|
| 181 |
} |
|---|
| 182 |
|
|---|
| 183 |
/// Ditto |
|---|
| 184 |
@property ref Tuple!(Fields) front() |
|---|
| 185 |
{ |
|---|
| 186 |
return current; |
|---|
| 187 |
} |
|---|
| 188 |
|
|---|
| 189 |
/// Ditto |
|---|
| 190 |
void popFront() |
|---|
| 191 |
{ |
|---|
| 192 |
enforce(file.isOpen); |
|---|
| 193 |
file.readln(line); |
|---|
| 194 |
if (!line.length) |
|---|
| 195 |
{ |
|---|
| 196 |
file.detach; |
|---|
| 197 |
} |
|---|
| 198 |
else |
|---|
| 199 |
{ |
|---|
| 200 |
line = chomp(line); |
|---|
| 201 |
formattedRead(line, format, ¤t); |
|---|
| 202 |
enforce(line.empty, text("Leftover characters in record: `", |
|---|
| 203 |
line, "'")); |
|---|
| 204 |
} |
|---|
| 205 |
} |
|---|
| 206 |
} |
|---|
| 207 |
|
|---|
| 208 |
template byRecord(Fields...) |
|---|
| 209 |
{ |
|---|
| 210 |
ByRecord!(Fields) byRecord(File f, string format) |
|---|
| 211 |
{ |
|---|
| 212 |
return typeof(return)(f, format); |
|---|
| 213 |
} |
|---|
| 214 |
} |
|---|
| 215 |
|
|---|
| 216 |
/** |
|---|
| 217 |
Encapsulates a $(D FILE*). Generally D does not attempt to provide |
|---|
| 218 |
thin wrappers over equivalent functions in the C standard library, but |
|---|
| 219 |
manipulating $(D FILE*) values directly is unsafe and error-prone in |
|---|
| 220 |
many ways. The $(D File) type ensures safe manipulation, automatic |
|---|
| 221 |
file closing, and a lot of convenience. |
|---|
| 222 |
|
|---|
| 223 |
The underlying $(D FILE*) handle is maintained in a reference-counted |
|---|
| 224 |
manner, such that as soon as the last $(D File) variable bound to a |
|---|
| 225 |
given $(D FILE*) goes out of scope, the underlying $(D FILE*) is |
|---|
| 226 |
automatically closed. |
|---|
| 227 |
|
|---|
| 228 |
Example: |
|---|
| 229 |
---- |
|---|
| 230 |
// test.d |
|---|
| 231 |
void main(string args[]) |
|---|
| 232 |
{ |
|---|
| 233 |
auto f = File("test.txt", "w"); // open for writing |
|---|
| 234 |
f.write("Hello"); |
|---|
| 235 |
if (args.length > 1) |
|---|
| 236 |
{ |
|---|
| 237 |
auto g = f; // now g and f write to the same file |
|---|
| 238 |
// internal reference count is 2 |
|---|
| 239 |
g.write(", ", args[1]); |
|---|
| 240 |
// g exits scope, reference count decreases to 1 |
|---|
| 241 |
} |
|---|
| 242 |
f.writeln("!"); |
|---|
| 243 |
// f exits scope, reference count falls to zero, |
|---|
| 244 |
// underlying $(D FILE*) is closed. |
|---|
| 245 |
} |
|---|
| 246 |
---- |
|---|
| 247 |
<pre class=console> |
|---|
| 248 |
% rdmd test.d Jimmy |
|---|
| 249 |
% cat test.txt |
|---|
| 250 |
Hello, Jimmy! |
|---|
| 251 |
% __ |
|---|
| 252 |
</pre> |
|---|
| 253 |
*/ |
|---|
| 254 |
struct File |
|---|
| 255 |
{ |
|---|
| 256 |
private struct Impl |
|---|
| 257 |
{ |
|---|
| 258 |
FILE * handle = null; |
|---|
| 259 |
uint refs = uint.max / 2; |
|---|
| 260 |
string name = null; |
|---|
| 261 |
bool isPipe; |
|---|
| 262 |
this(FILE* h, uint r, string n, bool pipe = false) |
|---|
| 263 |
{ |
|---|
| 264 |
handle = h; |
|---|
| 265 |
refs = r; |
|---|
| 266 |
name = n; |
|---|
| 267 |
isPipe = pipe; |
|---|
| 268 |
} |
|---|
| 269 |
} |
|---|
| 270 |
private Impl * p; |
|---|
| 271 |
|
|---|
| 272 |
/** |
|---|
| 273 |
Constructor taking the name of the file to open and the open mode |
|---|
| 274 |
(with the same semantics as in the C standard library $(WEB |
|---|
| 275 |
cplusplus.com/reference/clibrary/cstdio/fopen.html, fopen) |
|---|
| 276 |
function). Throws an exception if the file could not be opened. |
|---|
| 277 |
|
|---|
| 278 |
Copying one $(D File) object to another results in the two $(D File) |
|---|
| 279 |
objects referring to the same underlying file. |
|---|
| 280 |
|
|---|
| 281 |
The destructor automatically closes the file as soon as no $(D File) |
|---|
| 282 |
object refers to it anymore. |
|---|
| 283 |
*/ |
|---|
| 284 |
this(string name, in char[] stdioOpenmode = "rb") |
|---|
| 285 |
{ |
|---|
| 286 |
p = new Impl(errnoEnforce(.fopen(name, stdioOpenmode), |
|---|
| 287 |
text("Cannot open file `", name, "' in mode `", |
|---|
| 288 |
stdioOpenmode, "'")), |
|---|
| 289 |
1, name); |
|---|
| 290 |
} |
|---|
| 291 |
|
|---|
| 292 |
~this() |
|---|
| 293 |
{ |
|---|
| 294 |
if (!p) return; |
|---|
| 295 |
if (p.refs == 1) close; |
|---|
| 296 |
else --p.refs; |
|---|
| 297 |
} |
|---|
| 298 |
|
|---|
| 299 |
this(this) |
|---|
| 300 |
{ |
|---|
| 301 |
if (!p) return; |
|---|
| 302 |
assert(p.refs); |
|---|
| 303 |
++p.refs; |
|---|
| 304 |
} |
|---|
| 305 |
|
|---|
| 306 |
/** |
|---|
| 307 |
Assigns a file to another. The target of the assignment gets detached |
|---|
| 308 |
from whatever file it was attached to, and attaches itself to the new |
|---|
| 309 |
file. |
|---|
| 310 |
*/ |
|---|
| 311 |
void opAssign(File rhs) |
|---|
| 312 |
{ |
|---|
| 313 |
swap(p, rhs.p); |
|---|
| 314 |
} |
|---|
| 315 |
|
|---|
| 316 |
/** |
|---|
| 317 |
First calls $(D detach) (throwing on failure), and then attempts to |
|---|
| 318 |
_open file $(D name) with mode $(D stdioOpenmode). The mode has the |
|---|
| 319 |
same semantics as in the C standard library $(WEB |
|---|
| 320 |
cplusplus.com/reference/clibrary/cstdio/fopen.html, fopen) function. |
|---|
| 321 |
Throws exception in case of error. |
|---|
| 322 |
*/ |
|---|
| 323 |
void open(string name, in char[] stdioOpenmode = "rb") |
|---|
| 324 |
{ |
|---|
| 325 |
detach; |
|---|
| 326 |
auto another = File(name, stdioOpenmode); |
|---|
| 327 |
swap(this, another); |
|---|
| 328 |
} |
|---|
| 329 |
|
|---|
| 330 |
/** |
|---|
| 331 |
First calls $(D detach) (throwing on failure), and then runs a command |
|---|
| 332 |
by calling the C standard library function $(WEB |
|---|
| 333 |
opengroup.org/onlinepubs/007908799/xsh/_popen.html, _popen). |
|---|
| 334 |
*/ |
|---|
| 335 |
version(Posix) void popen(string command, in char[] stdioOpenmode = "r") |
|---|
| 336 |
{ |
|---|
| 337 |
detach; |
|---|
| 338 |
p = new Impl(errnoEnforce(.popen(command, stdioOpenmode), |
|---|
| 339 |
"Cannot run command `"~command~"'"), |
|---|
| 340 |
1, command, true); |
|---|
| 341 |
} |
|---|
| 342 |
|
|---|
| 343 |
/** Returns $(D true) if the file is opened. */ |
|---|
| 344 |
@property bool isOpen() const |
|---|
| 345 |
{ |
|---|
| 346 |
return p !is null && p.handle; |
|---|
| 347 |
} |
|---|
| 348 |
|
|---|
| 349 |
/** |
|---|
| 350 |
Returns $(D true) if the file is at end (see $(WEB |
|---|
| 351 |
cplusplus.com/reference/clibrary/cstdio/feof.html, feof)). The file |
|---|
| 352 |
must be opened, otherwise an exception is thrown. |
|---|
| 353 |
*/ |
|---|
| 354 |
@property bool eof() const |
|---|
| 355 |
{ |
|---|
| 356 |
enforce(p && p.handle, "Calling eof() against an unopened file."); |
|---|
| 357 |
return .feof(cast(FILE*) p.handle) != 0; |
|---|
| 358 |
} |
|---|
| 359 |
|
|---|
| 360 |
/** Returns the name of the file, if any. */ |
|---|
| 361 |
@property string name() const |
|---|
| 362 |
{ |
|---|
| 363 |
return p.name; |
|---|
| 364 |
} |
|---|
| 365 |
|
|---|
| 366 |
/** |
|---|
| 367 |
If the file is not opened, returns $(D false). Otherwise, returns |
|---|
| 368 |
$(WEB cplusplus.com/reference/clibrary/cstdio/ferror.html, ferror) for |
|---|
| 369 |
the file handle. |
|---|
| 370 |
*/ |
|---|
| 371 |
@property bool error() const |
|---|
| 372 |
{ |
|---|
| 373 |
return !p.handle || .ferror(cast(FILE*) p.handle); |
|---|
| 374 |
} |
|---|
| 375 |
|
|---|
| 376 |
/** |
|---|
| 377 |
Detaches from the underlying file. If the sole owner, calls $(D close) |
|---|
| 378 |
and throws if that fails. |
|---|
| 379 |
*/ |
|---|
| 380 |
void detach() |
|---|
| 381 |
{ |
|---|
| 382 |
if (!p) return; |
|---|
| 383 |
// @@@BUG |
|---|
| 384 |
//if (p.refs == 1) close; |
|---|
| 385 |
p = null; |
|---|
| 386 |
} |
|---|
| 387 |
|
|---|
| 388 |
/** |
|---|
| 389 |
If the file was unopened, succeeds vacuously. Otherwise closes the |
|---|
| 390 |
file (by calling $(WEB |
|---|
| 391 |
cplusplus.com/reference/clibrary/cstdio/fclose.html, fclose)), |
|---|
| 392 |
throwing on error. Even if an exception is thrown, afterwards the $(D |
|---|
| 393 |
File) object is empty. This is different from $(D detach) in that it |
|---|
| 394 |
always closes the file; consequently, all other $(D File) objects |
|---|
| 395 |
referring to the same handle will see a closed file henceforth. |
|---|
| 396 |
*/ |
|---|
| 397 |
void close() |
|---|
| 398 |
{ |
|---|
| 399 |
if (!p) return; // succeed vacuously |
|---|
| 400 |
if (!p.handle) |
|---|
| 401 |
{ |
|---|
| 402 |
p = null; // start a new life |
|---|
| 403 |
return; |
|---|
| 404 |
} |
|---|
| 405 |
scope(exit) |
|---|
| 406 |
{ |
|---|
| 407 |
p.handle = null; // nullify the handle anyway |
|---|
| 408 |
p.name = null; |
|---|
| 409 |
--p.refs; |
|---|
| 410 |
p = null; |
|---|
| 411 |
} |
|---|
| 412 |
version (Posix) |
|---|
| 413 |
{ |
|---|
| 414 |
if (p.isPipe) |
|---|
| 415 |
{ |
|---|
| 416 |
// Ignore the result of the command |
|---|
| 417 |
errnoEnforce(.pclose(p.handle) == 0, |
|---|
| 418 |
"Could not close pipe `"~p.name~"'"); |
|---|
| 419 |
return; |
|---|
| 420 |
} |
|---|
| 421 |
} |
|---|
| 422 |
//fprintf(std.c.stdio.stderr, ("Closing file `"~name~"`.\n\0").ptr); |
|---|
| 423 |
errnoEnforce(.fclose(p.handle) == 0, |
|---|
| 424 |
"Could not close file `"~p.name~"'"); |
|---|
| 425 |
} |
|---|
| 426 |
|
|---|
| 427 |
/** |
|---|
| 428 |
If the file is not opened, succeeds vacuously. Otherwise, returns |
|---|
| 429 |
$(WEB cplusplus.com/reference/clibrary/cstdio/_clearerr.html, |
|---|
| 430 |
_clearerr) for the file handle. |
|---|
| 431 |
*/ |
|---|
| 432 |
void clearerr() |
|---|
| 433 |
{ |
|---|
| 434 |
p is null || p.handle is null || |
|---|
| 435 |
.clearerr(p.handle); |
|---|
| 436 |
} |
|---|
| 437 |
|
|---|
| 438 |
/** |
|---|
| 439 |
If the file is not opened, throws an exception. Otherwise, calls $(WEB |
|---|
| 440 |
cplusplus.com/reference/clibrary/cstdio/_fflush.html, _fflush) for the |
|---|
| 441 |
file handle and throws on error. |
|---|
| 442 |
*/ |
|---|
| 443 |
void flush() |
|---|
| 444 |
{ |
|---|
| 445 |
errnoEnforce |
|---|
| 446 |
(.fflush(enforce(p.handle, "Calling fflush() on an unopened file")) |
|---|
| 447 |
== 0); |
|---|
| 448 |
} |
|---|
| 449 |
|
|---|
| 450 |
/** |
|---|
| 451 |
If the file is not opened, throws an exception. Otherwise, calls $(WEB |
|---|
| 452 |
cplusplus.com/reference/clibrary/cstdio/fread.html, fread) for the |
|---|
| 453 |
file handle and throws on error. |
|---|
| 454 |
|
|---|
| 455 |
$(D rawRead) always read in binary mode on Windows. |
|---|
| 456 |
*/ |
|---|
| 457 |
T[] rawRead(T)(T[] buffer) |
|---|
| 458 |
{ |
|---|
| 459 |
enforce(buffer.length, "rawRead must take a non-empty buffer"); |
|---|
| 460 |
version(Windows) |
|---|
| 461 |
{ |
|---|
| 462 |
immutable fd = ._fileno(p.handle); |
|---|
| 463 |
immutable mode = ._setmode(fd, _O_BINARY); |
|---|
| 464 |
scope(exit) ._setmode(fd, mode); |
|---|
| 465 |
version(DIGITAL_MARS_STDIO) |
|---|
| 466 |
{ |
|---|
| 467 |
// @@@BUG@@@ 4243 |
|---|
| 468 |
immutable info = __fhnd_info[fd]; |
|---|
| 469 |
__fhnd_info[fd] &= ~FHND_TEXT; |
|---|
| 470 |
scope(exit) __fhnd_info[fd] = info; |
|---|
| 471 |
} |
|---|
| 472 |
} |
|---|
| 473 |
immutable result = |
|---|
| 474 |
.fread(buffer.ptr, T.sizeof, buffer.length, p.handle); |
|---|
| 475 |
errnoEnforce(!error); |
|---|
| 476 |
return result ? buffer[0 .. result] : null; |
|---|
| 477 |
} |
|---|
| 478 |
|
|---|
| 479 |
unittest |
|---|
| 480 |
{ |
|---|
| 481 |
std.file.write("deleteme", "\r\n\n\r\n"); |
|---|
| 482 |
scope(exit) std.file.remove("deleteme"); |
|---|
| 483 |
auto f = File("deleteme", "r"); |
|---|
| 484 |
auto buf = f.rawRead(new char[5]); |
|---|
| 485 |
f.close(); |
|---|
| 486 |
assert(buf == "\r\n\n\r\n"); |
|---|
| 487 |
/+ |
|---|
| 488 |
buf = stdin.rawRead(new char[5]); |
|---|
| 489 |
assert(buf == "\r\n\n\r\n"); |
|---|
| 490 |
+/ |
|---|
| 491 |
} |
|---|
| 492 |
|
|---|
| 493 |
/** |
|---|
| 494 |
If the file is not opened, throws an exception. Otherwise, calls $(WEB |
|---|
| 495 |
cplusplus.com/reference/clibrary/cstdio/fwrite.html, fwrite) for the |
|---|
| 496 |
file handle and throws on error. |
|---|
| 497 |
|
|---|
| 498 |
$(D rawWrite) always write in binary mode on Windows. |
|---|
| 499 |
*/ |
|---|
| 500 |
void rawWrite(T)(in T[] buffer) |
|---|
| 501 |
{ |
|---|
| 502 |
version(Windows) |
|---|
| 503 |
{ |
|---|
| 504 |
flush(); // before changing translation mode |
|---|
| 505 |
immutable fd = ._fileno(p.handle); |
|---|
| 506 |
immutable mode = ._setmode(fd, _O_BINARY); |
|---|
| 507 |
scope(exit) ._setmode(fd, mode); |
|---|
| 508 |
version(DIGITAL_MARS_STDIO) |
|---|
| 509 |
{ |
|---|
| 510 |
// @@@BUG@@@ 4243 |
|---|
| 511 |
immutable info = __fhnd_info[fd]; |
|---|
| 512 |
__fhnd_info[fd] &= ~FHND_TEXT; |
|---|
| 513 |
scope(exit) __fhnd_info[fd] = info; |
|---|
| 514 |
} |
|---|
| 515 |
scope(exit) flush(); // before restoring translation mode |
|---|
| 516 |
} |
|---|
| 517 |
auto result = |
|---|
| 518 |
.fwrite(buffer.ptr, T.sizeof, buffer.length, p.handle); |
|---|
| 519 |
if (result == result.max) result = 0; |
|---|
| 520 |
errnoEnforce(result == buffer.length, |
|---|
| 521 |
text("Wrote ", result, " instead of ", buffer.length, |
|---|
| 522 |
" objects of type ", T.stringof, " to file `", |
|---|
| 523 |
p.name, "'")); |
|---|
| 524 |
} |
|---|
| 525 |
|
|---|
| 526 |
unittest |
|---|
| 527 |
{ |
|---|
| 528 |
auto f = File("deleteme", "w"); |
|---|
| 529 |
scope(exit) std.file.remove("deleteme"); |
|---|
| 530 |
f.rawWrite("\r\n\n\r\n"); |
|---|
| 531 |
f.close(); |
|---|
| 532 |
assert(std.file.read("deleteme") == "\r\n\n\r\n"); |
|---|
| 533 |
} |
|---|
| 534 |
|
|---|
| 535 |
/** |
|---|
| 536 |
If the file is not opened, throws an exception. Otherwise, calls $(WEB |
|---|
| 537 |
cplusplus.com/reference/clibrary/cstdio/fseek.html, fseek) for the |
|---|
| 538 |
file handle. Throws on error. |
|---|
| 539 |
*/ |
|---|
| 540 |
void seek(long offset, int origin = SEEK_SET) |
|---|
| 541 |
{ |
|---|
| 542 |
enforce(isOpen, "Attempting to seek() in an unopened file"); |
|---|
| 543 |
version (Windows) |
|---|
| 544 |
{ |
|---|
| 545 |
errnoEnforce(fseek(p.handle, to!int(offset), origin) == 0, |
|---|
| 546 |
"Could not seek in file `"~p.name~"'"); |
|---|
| 547 |
} |
|---|
| 548 |
else |
|---|
| 549 |
{ |
|---|
| 550 |
//static assert(off_t.sizeof == 8); |
|---|
| 551 |
errnoEnforce(fseeko(p.handle, offset, origin) == 0, |
|---|
| 552 |
"Could not seek in file `"~p.name~"'"); |
|---|
| 553 |
} |
|---|
| 554 |
} |
|---|
| 555 |
|
|---|
| 556 |
unittest |
|---|
| 557 |
{ |
|---|
| 558 |
auto f = File("deleteme", "w+"); |
|---|
| 559 |
scope(exit) { f.close(); std.file.remove("deleteme"); } |
|---|
| 560 |
f.rawWrite("abcdefghijklmnopqrstuvwxyz"); |
|---|
| 561 |
f.seek(7); |
|---|
| 562 |
assert(f.readln() == "hijklmnopqrstuvwxyz"); |
|---|
| 563 |
version (Windows) |
|---|
| 564 |
{ |
|---|
| 565 |
// No test for large files yet |
|---|
| 566 |
} |
|---|
| 567 |
else |
|---|
| 568 |
{ |
|---|
| 569 |
auto bigOffset = cast(ulong) int.max + 100; |
|---|
| 570 |
f.seek(bigOffset); |
|---|
| 571 |
assert(f.tell == bigOffset, text(f.tell)); |
|---|
| 572 |
// Uncomment the tests below only if you want to wait for |
|---|
| 573 |
// a long time |
|---|
| 574 |
// f.rawWrite("abcdefghijklmnopqrstuvwxyz"); |
|---|
| 575 |
// f.seek(-3, SEEK_END); |
|---|
| 576 |
// assert(f.readln() == "xyz"); |
|---|
| 577 |
} |
|---|
| 578 |
} |
|---|
| 579 |
|
|---|
| 580 |
/** |
|---|
| 581 |
If the file is not opened, throws an exception. Otherwise, calls $(WEB |
|---|
| 582 |
cplusplus.com/reference/clibrary/cstdio/ftell.html, ftell) for the |
|---|
| 583 |
managed file handle. Throws on error. |
|---|
| 584 |
*/ |
|---|
| 585 |
@property ulong tell() const |
|---|
| 586 |
{ |
|---|
| 587 |
enforce(isOpen, "Attempting to tell() in an unopened file"); |
|---|
| 588 |
version (Windows) |
|---|
| 589 |
{ |
|---|
| 590 |
immutable result = ftell(cast(FILE*) p.handle); |
|---|
| 591 |
} |
|---|
| 592 |
else |
|---|
| 593 |
{ |
|---|
| 594 |
immutable result = ftello(cast(FILE*) p.handle); |
|---|
| 595 |
} |
|---|
| 596 |
errnoEnforce(result != -1, |
|---|
| 597 |
"Query ftell() failed for file `"~p.name~"'"); |
|---|
| 598 |
return result; |
|---|
| 599 |
} |
|---|
| 600 |
|
|---|
| 601 |
unittest |
|---|
| 602 |
{ |
|---|
| 603 |
std.file.write("deleteme", "abcdefghijklmnopqrstuvwqxyz"); |
|---|
| 604 |
scope(exit) { std.file.remove("deleteme"); } |
|---|
| 605 |
auto f = File("deleteme"); |
|---|
| 606 |
auto a = new ubyte[4]; |
|---|
| 607 |
f.rawRead(a); |
|---|
| 608 |
assert(f.tell == 4, text(f.tell)); |
|---|
| 609 |
} |
|---|
| 610 |
|
|---|
| 611 |
/** |
|---|
| 612 |
If the file is not opened, throws an exception. Otherwise, calls $(WEB |
|---|
| 613 |
cplusplus.com/reference/clibrary/cstdio/_rewind.html, _rewind) for the |
|---|
| 614 |
file handle. Throws on error. |
|---|
| 615 |
*/ |
|---|
| 616 |
void rewind() |
|---|
| 617 |
{ |
|---|
| 618 |
enforce(isOpen, "Attempting to rewind() an unopened file"); |
|---|
| 619 |
.rewind(p.handle); |
|---|
| 620 |
} |
|---|
| 621 |
|
|---|
| 622 |
/** |
|---|
| 623 |
If the file is not opened, throws an exception. Otherwise, calls $(WEB |
|---|
| 624 |
cplusplus.com/reference/clibrary/cstdio/_setvbuf.html, _setvbuf) for |
|---|
| 625 |
the file handle. |
|---|
| 626 |
*/ |
|---|
| 627 |
void setvbuf(size_t size, int mode = _IOFBF) |
|---|
| 628 |
{ |
|---|
| 629 |
enforce(isOpen, "Attempting to call setvbuf() on an unopened file"); |
|---|
| 630 |
errnoEnforce(.setvbuf(p.handle, null, mode, size) == 0, |
|---|
| 631 |
"Could not set buffering for file `"~p.name~"'"); |
|---|
| 632 |
} |
|---|
| 633 |
|
|---|
| 634 |
/** |
|---|
| 635 |
If the file is not opened, throws an exception. Otherwise, calls |
|---|
| 636 |
$(WEB cplusplus.com/reference/clibrary/cstdio/_setvbuf.html, |
|---|
| 637 |
_setvbuf) for the file handle. */ |
|---|
| 638 |
void setvbuf(void[] buf, int mode = _IOFBF) |
|---|
| 639 |
{ |
|---|
| 640 |
enforce(isOpen, "Attempting to call setvbuf() on an unopened file"); |
|---|
| 641 |
errnoEnforce(.setvbuf(p.handle, |
|---|
| 642 |
cast(char*) buf.ptr, mode, buf.length) == 0, |
|---|
| 643 |
"Could not set buffering for file `"~p.name~"'"); |
|---|
| 644 |
} |
|---|
| 645 |
|
|---|
| 646 |
/** |
|---|
| 647 |
If the file is not opened, throws an exception. Otherwise, writes its |
|---|
| 648 |
arguments in text format to the file. */ |
|---|
| 649 |
void write(S...)(S args) |
|---|
| 650 |
{ |
|---|
| 651 |
auto w = lockingTextWriter(); |
|---|
| 652 |
foreach (arg; args) |
|---|
| 653 |
{ |
|---|
| 654 |
static if (isSomeString!(typeof(arg))) |
|---|
| 655 |
{ |
|---|
| 656 |
w.put(arg); |
|---|
| 657 |
} |
|---|
| 658 |
else |
|---|
| 659 |
{ |
|---|
| 660 |
std.format.formattedWrite(w, "%s", arg); |
|---|
| 661 |
} |
|---|
| 662 |
} |
|---|
| 663 |
} |
|---|
| 664 |
|
|---|
| 665 |
/** |
|---|
| 666 |
If the file is not opened, throws an exception. Otherwise, writes its |
|---|
| 667 |
arguments in text format to the file, followed by a newline. */ |
|---|
| 668 |
void writeln(S...)(S args) |
|---|
| 669 |
{ |
|---|
| 670 |
write(args, '\n'); |
|---|
| 671 |
errnoEnforce(.fflush(p.handle) == 0, |
|---|
| 672 |
"Could not flush file `"~p.name~"'"); |
|---|
| 673 |
} |
|---|
| 674 |
|
|---|
| 675 |
private enum errorMessage = |
|---|
| 676 |
"You must pass a formatting string as the first" |
|---|
| 677 |
" argument to writef or writefln. If no formatting is needed," |
|---|
| 678 |
" you may want to use write or writeln."; |
|---|
| 679 |
|
|---|
| 680 |
/** |
|---|
| 681 |
If the file is not opened, throws an exception. Otherwise, writes its |
|---|
| 682 |
arguments in text format to the file, according to the format in the |
|---|
| 683 |
first argument. */ |
|---|
| 684 |
void writef(S...)(S args)// if (isSomeString!(S[0])) |
|---|
| 685 |
{ |
|---|
| 686 |
assert(p); |
|---|
| 687 |
assert(p.handle); |
|---|
| 688 |
static assert(S.length>0, errorMessage); |
|---|
| 689 |
static assert(isSomeString!(S[0]), errorMessage); |
|---|
| 690 |
auto w = lockingTextWriter(); |
|---|
| 691 |
std.format.formattedWrite(w, args); |
|---|
| 692 |
} |
|---|
| 693 |
|
|---|
| 694 |
/** |
|---|
| 695 |
Same as writef, plus adds a newline. */ |
|---|
| 696 |
void writefln(S...)(S args) |
|---|
| 697 |
{ |
|---|
| 698 |
static assert(S.length>0, errorMessage); |
|---|
| 699 |
static assert(isSomeString!(S[0]), errorMessage); |
|---|
| 700 |
auto w = lockingTextWriter; |
|---|
| 701 |
std.format.formattedWrite(w, args); |
|---|
| 702 |
w.put('\n'); |
|---|
| 703 |
.fflush(p.handle); |
|---|
| 704 |
} |
|---|
| 705 |
|
|---|
| 706 |
/********************************** |
|---|
| 707 |
Read line from stream $(D fp) and write it to $(D buf[]), including |
|---|
| 708 |
terminating character. |
|---|
| 709 |
|
|---|
| 710 |
This is often faster than $(D File.readln(dchar)) because the buffer |
|---|
| 711 |
is reused each call. Note that reusing the buffer means that the |
|---|
| 712 |
previous contents of it has to be copied if needed. |
|---|
| 713 |
|
|---|
| 714 |
Params: |
|---|
| 715 |
fp = input stream |
|---|
| 716 |
buf = buffer used to store the resulting line data. buf is |
|---|
| 717 |
resized as necessary. |
|---|
| 718 |
|
|---|
| 719 |
Returns: |
|---|
| 720 |
0 for end of file, otherwise number of characters read |
|---|
| 721 |
|
|---|
| 722 |
Throws: $(D StdioException) on error |
|---|
| 723 |
|
|---|
| 724 |
Example: |
|---|
| 725 |
--- |
|---|
| 726 |
// Reads $(D stdin) and writes it to $(D stdout). |
|---|
| 727 |
import std.stdio; |
|---|
| 728 |
|
|---|
| 729 |
int main() |
|---|
| 730 |
{ |
|---|
| 731 |
char[] buf; |
|---|
| 732 |
while (stdin.readln(buf)) |
|---|
| 733 |
write(buf); |
|---|
| 734 |
return 0; |
|---|
| 735 |
} |
|---|
| 736 |
--- |
|---|
| 737 |
|
|---|
| 738 |
This method is more efficient than the one in the previous example |
|---|
| 739 |
because $(D stdin.readln(buf)) reuses (if possible) memory allocated |
|---|
| 740 |
by $(D buf), whereas $(D buf = stdin.readln()) makes a new memory allocation |
|---|
| 741 |
with every line. */ |
|---|
| 742 |
S readln(S = string)(dchar terminator = '\n') |
|---|
| 743 |
{ |
|---|
| 744 |
Unqual!(typeof(S.init[0]))[] buf; |
|---|
| 745 |
readln(buf, terminator); |
|---|
| 746 |
return assumeUnique(buf); |
|---|
| 747 |
} |
|---|
| 748 |
|
|---|
| 749 |
unittest |
|---|
| 750 |
{ |
|---|
| 751 |
std.file.write("deleteme", "hello\nworld\n"); |
|---|
| 752 |
scope(exit) std.file.remove("deleteme"); |
|---|
| 753 |
foreach (C; Tuple!(char, wchar, dchar).Types) |
|---|
| 754 |
{ |
|---|
| 755 |
auto witness = [ "hello\n", "world\n" ]; |
|---|
| 756 |
auto f = File("deleteme"); |
|---|
| 757 |
uint i = 0; |
|---|
| 758 |
immutable(C)[] buf; |
|---|
| 759 |
while ((buf = f.readln!(typeof(buf))()).length) |
|---|
| 760 |
{ |
|---|
| 761 |
assert(i < witness.length); |
|---|
| 762 |
assert(equal(buf, witness[i++])); |
|---|
| 763 |
} |
|---|
| 764 |
assert(i == witness.length); |
|---|
| 765 |
} |
|---|
| 766 |
} |
|---|
| 767 |
|
|---|
| 768 |
/** ditto */ |
|---|
| 769 |
size_t readln(C)(ref C[] buf, dchar terminator = '\n') if (isSomeChar!C) |
|---|
| 770 |
{ |
|---|
| 771 |
static if (is(C == char)) |
|---|
| 772 |
{ |
|---|
| 773 |
enforce(p && p.handle, "Attempt to read from an unopened file."); |
|---|
| 774 |
return readlnImpl(p.handle, buf, terminator); |
|---|
| 775 |
} |
|---|
| 776 |
else |
|---|
| 777 |
{ |
|---|
| 778 |
// TODO: optimize this |
|---|
| 779 |
string s = readln(terminator); |
|---|
| 780 |
if (!s.length) return 0; |
|---|
| 781 |
buf.length = 0; |
|---|
| 782 |
foreach (wchar c; s) |
|---|
| 783 |
{ |
|---|
| 784 |
buf ~= c; |
|---|
| 785 |
} |
|---|
| 786 |
return buf.length; |
|---|
| 787 |
} |
|---|
| 788 |
} |
|---|
| 789 |
|
|---|
| 790 |
/** ditto */ |
|---|
| 791 |
size_t readln(C, R)(ref C[] buf, R terminator) |
|---|
| 792 |
if (isBidirectionalRange!R && is(typeof(terminator.front == buf[0]))) |
|---|
| 793 |
{ |
|---|
| 794 |
auto last = terminator.back(); |
|---|
| 795 |
C[] buf2; |
|---|
| 796 |
swap(buf, buf2); |
|---|
| 797 |
for (;;) { |
|---|
| 798 |
if (!readln(buf2, last) || endsWith(buf2, terminator)) { |
|---|
| 799 |
if (buf.empty) { |
|---|
| 800 |
buf = buf2; |
|---|
| 801 |
} else { |
|---|
| 802 |
buf ~= buf2; |
|---|
| 803 |
} |
|---|
| 804 |
break; |
|---|
| 805 |
} |
|---|
| 806 |
buf ~= buf2; |
|---|
| 807 |
} |
|---|
| 808 |
return buf.length; |
|---|
| 809 |
} |
|---|
| 810 |
|
|---|
| 811 |
unittest |
|---|
| 812 |
{ |
|---|
| 813 |
std.file.write("deleteme", "hello\n\rworld\nhow\n\rare ya"); |
|---|
| 814 |
auto witness = [ "hello\n\r", "world\nhow\n\r", "are ya" ]; |
|---|
| 815 |
scope(exit) std.file.remove("deleteme"); |
|---|
| 816 |
auto f = File("deleteme"); |
|---|
| 817 |
uint i = 0; |
|---|
| 818 |
char[] buf; |
|---|
| 819 |
while (f.readln(buf, "\n\r")) |
|---|
| 820 |
{ |
|---|
| 821 |
assert(i < witness.length); |
|---|
| 822 |
assert(buf == witness[i++]); |
|---|
| 823 |
} |
|---|
| 824 |
} |
|---|
| 825 |
|
|---|
| 826 |
uint readf(Data...)(in char[] format, Data data) |
|---|
| 827 |
{ |
|---|
| 828 |
assert(isOpen); |
|---|
| 829 |
auto input = LockingTextReader(this); |
|---|
| 830 |
return formattedRead(input, format, data); |
|---|
| 831 |
} |
|---|
| 832 |
|
|---|
| 833 |
unittest |
|---|
| 834 |
{ |
|---|
| 835 |
std.file.write("deleteme", "hello\nworld\n"); |
|---|
| 836 |
scope(exit) std.file.remove("deleteme"); |
|---|
| 837 |
string s; |
|---|
| 838 |
auto f = File("deleteme"); |
|---|
| 839 |
f.readf("%s\n", &s); |
|---|
| 840 |
assert(s == "hello", "["~s~"]"); |
|---|
| 841 |
} |
|---|
| 842 |
|
|---|
| 843 |
/** |
|---|
| 844 |
Returns a temporary file by calling $(WEB |
|---|
| 845 |
cplusplus.com/reference/clibrary/cstdio/_tmpfile.html, _tmpfile). */ |
|---|
| 846 |
static File tmpfile() |
|---|
| 847 |
{ |
|---|
| 848 |
auto h = errnoEnforce(core.stdc.stdio.tmpfile, |
|---|
| 849 |
"Could not create temporary file with tmpfile()"); |
|---|
| 850 |
File result = void; |
|---|
| 851 |
result.p = new Impl(h, 1, null); |
|---|
| 852 |
return result; |
|---|
| 853 |
} |
|---|
| 854 |
|
|---|
| 855 |
/** |
|---|
| 856 |
Unsafe function that wraps an existing $(D FILE*). The resulting $(D |
|---|
| 857 |
File) never takes the initiative in closing the file. */ |
|---|
| 858 |
/*private*/ static File wrapFile(FILE* f) |
|---|
| 859 |
{ |
|---|
| 860 |
File result = void; |
|---|
| 861 |
//result.p = new Impl(f, uint.max / 2, null); |
|---|
| 862 |
result.p = new Impl(f, 9999, null); |
|---|
| 863 |
return result; |
|---|
| 864 |
} |
|---|
| 865 |
|
|---|
| 866 |
/** |
|---|
| 867 |
Returns the $(D FILE*) corresponding to this object. |
|---|
| 868 |
*/ |
|---|
| 869 |
FILE* getFP() |
|---|
| 870 |
{ |
|---|
| 871 |
enforce(p && p.handle, |
|---|
| 872 |
"Attempting to call getFP() on an unopened file"); |
|---|
| 873 |
return p.handle; |
|---|
| 874 |
} |
|---|
| 875 |
|
|---|
| 876 |
unittest |
|---|
| 877 |
{ |
|---|
| 878 |
assert(stdout.getFP == std.c.stdio.stdout); |
|---|
| 879 |
} |
|---|
| 880 |
|
|---|
| 881 |
/** |
|---|
| 882 |
Returns the file number corresponding to this object. |
|---|
| 883 |
*/ |
|---|
| 884 |
/*version(Posix) */int fileno() const |
|---|
| 885 |
{ |
|---|
| 886 |
enforce(isOpen, "Attempting to call fileno() on an unopened file"); |
|---|
| 887 |
return .fileno(cast(FILE*) p.handle); |
|---|
| 888 |
} |
|---|
| 889 |
|
|---|
| 890 |
/** |
|---|
| 891 |
Range that reads one line at a time. */ |
|---|
| 892 |
enum KeepTerminator : bool { no, yes } |
|---|
| 893 |
/// ditto |
|---|
| 894 |
struct ByLine(Char, Terminator) |
|---|
| 895 |
{ |
|---|
| 896 |
File file; |
|---|
| 897 |
Char[] line; |
|---|
| 898 |
Terminator terminator; |
|---|
| 899 |
KeepTerminator keepTerminator; |
|---|
| 900 |
|
|---|
| 901 |
this(File f, KeepTerminator kt = KeepTerminator.no, |
|---|
| 902 |
Terminator terminator = '\n') |
|---|
| 903 |
{ |
|---|
| 904 |
file = f; |
|---|
| 905 |
this.terminator = terminator; |
|---|
| 906 |
keepTerminator = kt; |
|---|
| 907 |
popFront; // prime the range |
|---|
| 908 |
// @@@BUG@@@ line below should not exist |
|---|
| 909 |
//if (file.p) ++file.p.refs; |
|---|
| 910 |
} |
|---|
| 911 |
|
|---|
| 912 |
/// Range primitive implementations. |
|---|
| 913 |
bool empty() const |
|---|
| 914 |
{ |
|---|
| 915 |
return !file.isOpen; |
|---|
| 916 |
} |
|---|
| 917 |
|
|---|
| 918 |
/// Ditto |
|---|
| 919 |
Char[] front() |
|---|
| 920 |
{ |
|---|
| 921 |
return line; |
|---|
| 922 |
} |
|---|
| 923 |
|
|---|
| 924 |
/// Ditto |
|---|
| 925 |
void popFront() |
|---|
| 926 |
{ |
|---|
| 927 |
enforce(file.isOpen); |
|---|
| 928 |
file.readln(line, terminator); |
|---|
| 929 |
if (!line.length) |
|---|
| 930 |
file.detach; |
|---|
| 931 |
else if (keepTerminator == KeepTerminator.no |
|---|
| 932 |
&& std.algorithm.endsWith(line, terminator)) |
|---|
| 933 |
line.length = line.length - 1; |
|---|
| 934 |
} |
|---|
| 935 |
} |
|---|
| 936 |
|
|---|
| 937 |
/** |
|---|
| 938 |
Convenience function that returns the $(D LinesReader) corresponding |
|---|
| 939 |
to this file. */ |
|---|
| 940 |
ByLine!(Char, Terminator) byLine(Terminator = char, Char = char) |
|---|
| 941 |
(KeepTerminator keepTerminator = KeepTerminator.no, |
|---|
| 942 |
Terminator terminator = '\n') |
|---|
| 943 |
{ |
|---|
| 944 |
return typeof(return)(this, keepTerminator, terminator); |
|---|
| 945 |
} |
|---|
| 946 |
|
|---|
| 947 |
unittest |
|---|
| 948 |
{ |
|---|
| 949 |
//printf("Entering test at line %d\n", __LINE__); |
|---|
| 950 |
scope(failure) printf("Failed test at line %d\n", __LINE__); |
|---|
| 951 |
std.file.write("testingByLine", "asd\ndef\nasdf"); |
|---|
| 952 |
auto witness = [ "asd", "def", "asdf" ]; |
|---|
| 953 |
uint i; |
|---|
| 954 |
auto f = File("testingByLine"); |
|---|
| 955 |
scope(exit) |
|---|
| 956 |
{ |
|---|
| 957 |
f.close; |
|---|
| 958 |
assert(!f.isOpen); |
|---|
| 959 |
//std.file.remove("testingByLine"); |
|---|
| 960 |
} |
|---|
| 961 |
foreach (line; f.byLine()) |
|---|
| 962 |
{ |
|---|
| 963 |
assert(line == witness[i++]); |
|---|
| 964 |
} |
|---|
| 965 |
assert(i == witness.length); |
|---|
| 966 |
i = 0; |
|---|
| 967 |
f.rewind; |
|---|
| 968 |
foreach (line; f.byLine(KeepTerminator.yes)) |
|---|
| 969 |
{ |
|---|
| 970 |
assert(line == witness[i++] ~ '\n' || i == witness.length); |
|---|
| 971 |
} |
|---|
| 972 |
assert(i == witness.length); |
|---|
| 973 |
} |
|---|
| 974 |
|
|---|
| 975 |
template byRecord(Fields...) |
|---|
| 976 |
{ |
|---|
| 977 |
ByRecord!(Fields) byRecord(string format) |
|---|
| 978 |
{ |
|---|
| 979 |
return typeof(return)(this, format); |
|---|
| 980 |
} |
|---|
| 981 |
} |
|---|
| 982 |
|
|---|
| 983 |
unittest |
|---|
| 984 |
{ |
|---|
| 985 |
// scope(failure) printf("Failed test at line %d\n", __LINE__); |
|---|
| 986 |
// std.file.write("deleteme", "1 2\n4 1\n5 100"); |
|---|
| 987 |
// scope(exit) std.file.remove("deleteme"); |
|---|
| 988 |
// File f = File("deleteme"); |
|---|
| 989 |
// scope(exit) f.close; |
|---|
| 990 |
// auto t = [ tuple(1, 2), tuple(4, 1), tuple(5, 100) ]; |
|---|
| 991 |
// uint i; |
|---|
| 992 |
// foreach (e; f.byRecord!(int, int)("%s %s")) |
|---|
| 993 |
// { |
|---|
| 994 |
// //.writeln(e); |
|---|
| 995 |
// assert(e == t[i++]); |
|---|
| 996 |
// } |
|---|
| 997 |
} |
|---|
| 998 |
|
|---|
| 999 |
|
|---|
| 1000 |
/** |
|---|
| 1001 |
* Range that reads a chunk at a time. |
|---|
| 1002 |
*/ |
|---|
| 1003 |
struct ByChunk |
|---|
| 1004 |
{ |
|---|
| 1005 |
private: |
|---|
| 1006 |
File file_; |
|---|
| 1007 |
ubyte[] chunk_; |
|---|
| 1008 |
|
|---|
| 1009 |
|
|---|
| 1010 |
public: |
|---|
| 1011 |
this(File file, size_t size) |
|---|
| 1012 |
in |
|---|
| 1013 |
{ |
|---|
| 1014 |
assert(size, "size must be larger than 0"); |
|---|
| 1015 |
} |
|---|
| 1016 |
body |
|---|
| 1017 |
{ |
|---|
| 1018 |
file_ = file; |
|---|
| 1019 |
chunk_ = new ubyte[](size); |
|---|
| 1020 |
|
|---|
| 1021 |
popFront(); |
|---|
| 1022 |
} |
|---|
| 1023 |
|
|---|
| 1024 |
|
|---|
| 1025 |
/// Range primitive operations. |
|---|
| 1026 |
@property |
|---|
| 1027 |
bool empty() const |
|---|
| 1028 |
{ |
|---|
| 1029 |
return !file_.isOpen; |
|---|
| 1030 |
} |
|---|
| 1031 |
|
|---|
| 1032 |
|
|---|
| 1033 |
/// Ditto |
|---|
| 1034 |
@property |
|---|
| 1035 |
nothrow ubyte[] front() |
|---|
| 1036 |
{ |
|---|
| 1037 |
return chunk_; |
|---|
| 1038 |
} |
|---|
| 1039 |
|
|---|
| 1040 |
|
|---|
| 1041 |
/// Ditto |
|---|
| 1042 |
void popFront() |
|---|
| 1043 |
{ |
|---|
| 1044 |
enforce(!empty, "Cannot call popFront on empty range"); |
|---|
| 1045 |
|
|---|
| 1046 |
chunk_ = file_.rawRead(chunk_); |
|---|
| 1047 |
if (chunk_.length == 0) |
|---|
| 1048 |
file_.detach(); |
|---|
| 1049 |
} |
|---|
| 1050 |
} |
|---|
| 1051 |
|
|---|
| 1052 |
/** |
|---|
| 1053 |
Iterates through a file a chunk at a time by using $(D foreach). |
|---|
| 1054 |
|
|---|
| 1055 |
Example: |
|---|
| 1056 |
|
|---|
| 1057 |
--------- |
|---|
| 1058 |
void main() |
|---|
| 1059 |
{ |
|---|
| 1060 |
foreach (ubyte[] buffer; stdin.byChunk(4096)) |
|---|
| 1061 |
{ |
|---|
| 1062 |
... use buffer ... |
|---|
| 1063 |
} |
|---|
| 1064 |
} |
|---|
| 1065 |
--------- |
|---|
| 1066 |
|
|---|
| 1067 |
The content of $(D buffer) is reused across calls. In the example |
|---|
| 1068 |
above, $(D buffer.length) is 4096 for all iterations, except for the |
|---|
| 1069 |
last one, in which case $(D buffer.length) may be less than 4096 (but |
|---|
| 1070 |
always greater than zero). |
|---|
| 1071 |
|
|---|
| 1072 |
In case of an I/O error, an $(D StdioException) is thrown. |
|---|
| 1073 |
*/ |
|---|
| 1074 |
ByChunk byChunk(size_t chunkSize) |
|---|
| 1075 |
{ |
|---|
| 1076 |
return ByChunk(this, chunkSize); |
|---|
| 1077 |
} |
|---|
| 1078 |
|
|---|
| 1079 |
unittest |
|---|
| 1080 |
{ |
|---|
| 1081 |
scope(failure) printf("Failed test at line %d\n", __LINE__); |
|---|
| 1082 |
|
|---|
| 1083 |
std.file.write("testingByChunk", "asd\ndef\nasdf"); |
|---|
| 1084 |
|
|---|
| 1085 |
auto witness = ["asd\n", "def\n", "asdf" ]; |
|---|
| 1086 |
auto f = File("testingByChunk"); |
|---|
| 1087 |
scope(exit) |
|---|
| 1088 |
{ |
|---|
| 1089 |
f.close; |
|---|
| 1090 |
assert(!f.isOpen); |
|---|
| 1091 |
std.file.remove("testingByChunk"); |
|---|
| 1092 |
} |
|---|
| 1093 |
|
|---|
| 1094 |
uint i; |
|---|
| 1095 |
foreach (chunk; f.byChunk(4)) |
|---|
| 1096 |
assert(chunk == cast(ubyte[])witness[i++]); |
|---|
| 1097 |
|
|---|
| 1098 |
assert(i == witness.length); |
|---|
| 1099 |
} |
|---|
| 1100 |
|
|---|
| 1101 |
/** |
|---|
| 1102 |
$(D Range) that locks the file and allows fast writing to it. |
|---|
| 1103 |
*/ |
|---|
| 1104 |
struct LockingTextWriter |
|---|
| 1105 |
{ |
|---|
| 1106 |
FILE* fps; // the shared file handle |
|---|
| 1107 |
_iobuf* handle; // the unshared version of fps |
|---|
| 1108 |
int orientation; |
|---|
| 1109 |
|
|---|
| 1110 |
this(ref File f) |
|---|
| 1111 |
{ |
|---|
| 1112 |
enforce(f.p && f.p.handle); |
|---|
| 1113 |
fps = f.p.handle; |
|---|
| 1114 |
orientation = fwide(fps, 0); |
|---|
| 1115 |
FLOCK(fps); |
|---|
| 1116 |
handle = cast(_iobuf*)fps; |
|---|
| 1117 |
} |
|---|
| 1118 |
|
|---|
| 1119 |
~this() |
|---|
| 1120 |
{ |
|---|
| 1121 |
FUNLOCK(fps); |
|---|
| 1122 |
fps = null; |
|---|
| 1123 |
handle = null; |
|---|
| 1124 |
} |
|---|
| 1125 |
|
|---|
| 1126 |
this(this) |
|---|
| 1127 |
{ |
|---|
| 1128 |
enforce(fps); |
|---|
| 1129 |
FLOCK(fps); |
|---|
| 1130 |
} |
|---|
| 1131 |
|
|---|
| 1132 |
/// Range primitive implementations. |
|---|
| 1133 |
void put(A)(A writeme) if (is(ElementType!A : const(dchar))) |
|---|
| 1134 |
{ |
|---|
| 1135 |
static if (isSomeString!A) |
|---|
| 1136 |
alias typeof(writeme[0]) C; |
|---|
| 1137 |
else |
|---|
| 1138 |
alias ElementType!A C; |
|---|
| 1139 |
static assert(!is(C == void)); |
|---|
| 1140 |
if (writeme[0].sizeof == 1 && orientation <= 0) |
|---|
| 1141 |
{ |
|---|
| 1142 |
//file.write(writeme); causes infinite recursion!!! |
|---|
| 1143 |
//file.rawWrite(writeme); |
|---|
| 1144 |
auto result = |
|---|
| 1145 |
.fwrite(writeme.ptr, C.sizeof, writeme.length, fps); |
|---|
| 1146 |
if (result != writeme.length) errnoEnforce(0); |
|---|
| 1147 |
} |
|---|
| 1148 |
else |
|---|
| 1149 |
{ |
|---|
| 1150 |
// put each character in turn |
|---|
| 1151 |
foreach (dchar c; writeme) |
|---|
| 1152 |
{ |
|---|
| 1153 |
put(c); |
|---|
| 1154 |
} |
|---|
| 1155 |
} |
|---|
| 1156 |
} |
|---|
| 1157 |
|
|---|
| 1158 |
// @@@BUG@@@ 2340 |
|---|
| 1159 |
//void front(C)(C c) if (is(C : dchar)) { |
|---|
| 1160 |
/// ditto |
|---|
| 1161 |
void put(C)(C c) if (is(C : const(dchar))) |
|---|
| 1162 |
{ |
|---|
| 1163 |
static if (c.sizeof == 1) |
|---|
| 1164 |
{ |
|---|
| 1165 |
// simple char |
|---|
| 1166 |
if (orientation <= 0) FPUTC(c, handle); |
|---|
| 1167 |
else FPUTWC(c, handle); |
|---|
| 1168 |
} |
|---|
| 1169 |
else static if (c.sizeof == 2) |
|---|
| 1170 |
{ |
|---|
| 1171 |
if (orientation <= 0) |
|---|
| 1172 |
{ |
|---|
| 1173 |
if (c <= 0x7F) |
|---|
| 1174 |
{ |
|---|
| 1175 |
FPUTC(c, handle); |
|---|
| 1176 |
} |
|---|
| 1177 |
else |
|---|
| 1178 |
{ |
|---|
| 1179 |
char[4] buf; |
|---|
| 1180 |
auto b = std.utf.toUTF8(buf, c); |
|---|
| 1181 |
foreach (i ; 0 .. b.length) |
|---|
| 1182 |
FPUTC(b[i], handle); |
|---|
| 1183 |
} |
|---|
| 1184 |
} |
|---|
| 1185 |
else |
|---|
| 1186 |
{ |
|---|
| 1187 |
FPUTWC(c, handle); |
|---|
| 1188 |
} |
|---|
| 1189 |
} |
|---|
| 1190 |
else // 32-bit characters |
|---|
| 1191 |
{ |
|---|
| 1192 |
if (orientation <= 0) |
|---|
| 1193 |
{ |
|---|
| 1194 |
if (c <= 0x7F) |
|---|
| 1195 |
{ |
|---|
| 1196 |
FPUTC(c, handle); |
|---|
| 1197 |
} |
|---|
| 1198 |
else |
|---|
| 1199 |
{ |
|---|
| 1200 |
char[4] buf = void; |
|---|
| 1201 |
auto b = std.utf.toUTF8(buf, c); |
|---|
| 1202 |
foreach (i ; 0 .. b.length) |
|---|
| 1203 |
FPUTC(b[i], handle); |
|---|
| 1204 |
} |
|---|
| 1205 |
} |
|---|
| 1206 |
else |
|---|
| 1207 |
{ |
|---|
| 1208 |
version (Windows) |
|---|
| 1209 |
{ |
|---|
| 1210 |
assert(isValidDchar(c)); |
|---|
| 1211 |
if (c <= 0xFFFF) |
|---|
| 1212 |
{ |
|---|
| 1213 |
FPUTWC(c, handle); |
|---|
| 1214 |
} |
|---|
| 1215 |
else |
|---|
| 1216 |
{ |
|---|
| 1217 |
FPUTWC(cast(wchar) |
|---|
| 1218 |
((((c - 0x10000) >> 10) & 0x3FF) |
|---|
| 1219 |
+ 0xD800), handle); |
|---|
| 1220 |
FPUTWC(cast(wchar) |
|---|
| 1221 |
(((c - 0x10000) & 0x3FF) + 0xDC00), |
|---|
| 1222 |
handle); |
|---|
| 1223 |
} |
|---|
| 1224 |
} |
|---|
| 1225 |
else version (Posix) |
|---|
| 1226 |
{ |
|---|
| 1227 |
FPUTWC(c, handle); |
|---|
| 1228 |
} |
|---|
| 1229 |
else |
|---|
| 1230 |
{ |
|---|
| 1231 |
static assert(0); |
|---|
| 1232 |
} |
|---|
| 1233 |
} |
|---|
| 1234 |
} |
|---|
| 1235 |
} |
|---|
| 1236 |
} |
|---|
| 1237 |
|
|---|
| 1238 |
/// Convenience function. |
|---|
| 1239 |
LockingTextWriter lockingTextWriter() |
|---|
| 1240 |
{ |
|---|
| 1241 |
return LockingTextWriter(this); |
|---|
| 1242 |
} |
|---|
| 1243 |
|
|---|
| 1244 |
/// Get the size of the file, ulong.max if file is not searchable, but still throws if an actual error occurs. |
|---|
| 1245 |
@property ulong size() |
|---|
| 1246 |
{ |
|---|
| 1247 |
ulong pos = void; |
|---|
| 1248 |
if (collectException(pos = tell)) return ulong.max; |
|---|
| 1249 |
scope(exit) seek(pos); |
|---|
| 1250 |
seek(0, SEEK_END); |
|---|
| 1251 |
return tell; |
|---|
| 1252 |
} |
|---|
| 1253 |
} |
|---|
| 1254 |
|
|---|
| 1255 |
unittest |
|---|
| 1256 |
{ |
|---|
| 1257 |
std.file.write("deleteme", "1 2 3"); |
|---|
| 1258 |
auto f = File("deleteme"); |
|---|
| 1259 |
assert(f.size == 5); |
|---|
| 1260 |
assert(f.tell == 0); |
|---|
| 1261 |
} |
|---|
| 1262 |
|
|---|
| 1263 |
struct LockingTextReader |
|---|
| 1264 |
{ |
|---|
| 1265 |
private File _f; |
|---|
| 1266 |
private dchar _crt; |
|---|
| 1267 |
|
|---|
| 1268 |
this(File f) |
|---|
| 1269 |
{ |
|---|
| 1270 |
enforce(f.isOpen); |
|---|
| 1271 |
_f = f; |
|---|
| 1272 |
FLOCK(_f.p.handle); |
|---|
| 1273 |
} |
|---|
| 1274 |
|
|---|
| 1275 |
this(this) |
|---|
| 1276 |
{ |
|---|
| 1277 |
FLOCK(_f.p.handle); |
|---|
| 1278 |
} |
|---|
| 1279 |
|
|---|
| 1280 |
~this() |
|---|
| 1281 |
{ |
|---|
| 1282 |
// File locking has its own reference count |
|---|
| 1283 |
if (_f.isOpen) FUNLOCK(_f.p.handle); |
|---|
| 1284 |
} |
|---|
| 1285 |
|
|---|
| 1286 |
void opAssign(LockingTextReader r) |
|---|
| 1287 |
{ |
|---|
| 1288 |
swap(this, r); |
|---|
| 1289 |
} |
|---|
| 1290 |
|
|---|
| 1291 |
@property bool empty() |
|---|
| 1292 |
{ |
|---|
| 1293 |
if (!_f.isOpen || _f.eof) return true; |
|---|
| 1294 |
if (_crt == _crt.init) |
|---|
| 1295 |
{ |
|---|
| 1296 |
_crt = FGETC(cast(_iobuf*) _f.p.handle); |
|---|
| 1297 |
if (_crt == -1) |
|---|
| 1298 |
{ |
|---|
| 1299 |
clear(_f); |
|---|
| 1300 |
return true; |
|---|
| 1301 |
} |
|---|
| 1302 |
else |
|---|
| 1303 |
{ |
|---|
| 1304 |
enforce(ungetc(_crt, cast(FILE*) _f.p.handle) == _crt); |
|---|
| 1305 |
} |
|---|
| 1306 |
} |
|---|
| 1307 |
return false; |
|---|
| 1308 |
} |
|---|
| 1309 |
|
|---|
| 1310 |
dchar front() |
|---|
| 1311 |
{ |
|---|
| 1312 |
enforce(!empty); |
|---|
| 1313 |
return _crt; |
|---|
| 1314 |
} |
|---|
| 1315 |
|
|---|
| 1316 |
void popFront() |
|---|
| 1317 |
{ |
|---|
| 1318 |
enforce(!empty); |
|---|
| 1319 |
if (FGETC(cast(_iobuf*) _f.p.handle) == -1) |
|---|
| 1320 |
{ |
|---|
| 1321 |
enforce(_f.eof); |
|---|
| 1322 |
} |
|---|
| 1323 |
_crt = _crt.init; |
|---|
| 1324 |
} |
|---|
| 1325 |
|
|---|
| 1326 |
// void unget(dchar c) |
|---|
| 1327 |
// { |
|---|
| 1328 |
// ungetc(c, cast(FILE*) _f.p.handle); |
|---|
| 1329 |
// } |
|---|
| 1330 |
} |
|---|
| 1331 |
|
|---|
| 1332 |
unittest |
|---|
| 1333 |
{ |
|---|
| 1334 |
std.file.write("deleteme", "1 2 3"); |
|---|
| 1335 |
int x, y; |
|---|
| 1336 |
auto f = File("deleteme"); |
|---|
| 1337 |
f.readf("%s ", &x); |
|---|
| 1338 |
assert(x == 1); |
|---|
| 1339 |
f.readf("%d ", &x); |
|---|
| 1340 |
assert(x == 2); |
|---|
| 1341 |
f.readf("%d ", &x); |
|---|
| 1342 |
assert(x == 3); |
|---|
| 1343 |
//pragma(msg, "--- todo: readf ---"); |
|---|
| 1344 |
} |
|---|
| 1345 |
|
|---|
| 1346 |
private |
|---|
| 1347 |
void writefx(FILE* fps, TypeInfo[] arguments, void* argptr, int newline=false) |
|---|
| 1348 |
{ |
|---|
| 1349 |
int orientation = fwide(fps, 0); // move this inside the lock? |
|---|
| 1350 |
|
|---|
| 1351 |
/* Do the file stream locking at the outermost level |
|---|
| 1352 |
* rather than character by character. |
|---|
| 1353 |
*/ |
|---|
| 1354 |
FLOCK(fps); |
|---|
| 1355 |
scope(exit) FUNLOCK(fps); |
|---|
| 1356 |
|
|---|
| 1357 |
auto fp = cast(_iobuf*)fps; // fp is locked version |
|---|
| 1358 |
|
|---|
| 1359 |
if (orientation <= 0) // byte orientation or no orientation |
|---|
| 1360 |
{ |
|---|
| 1361 |
void putc(dchar c) |
|---|
| 1362 |
{ |
|---|
| 1363 |
if (c <= 0x7F) |
|---|
| 1364 |
{ |
|---|
| 1365 |
FPUTC(c, fp); |
|---|
| 1366 |
} |
|---|
| 1367 |
else |
|---|
| 1368 |
{ |
|---|
| 1369 |
char[4] buf = void; |
|---|
| 1370 |
foreach (i; 0 .. std.utf.toUTF8(buf, c).length) |
|---|
| 1371 |
FPUTC(buf[i], fp); |
|---|
| 1372 |
} |
|---|
| 1373 |
} |
|---|
| 1374 |
|
|---|
| 1375 |
std.format.doFormat(&putc, arguments, argptr); |
|---|
| 1376 |
if (newline) |
|---|
| 1377 |
FPUTC('\n', fp); |
|---|
| 1378 |
} |
|---|
| 1379 |
else if (orientation > 0) // wide orientation |
|---|
| 1380 |
{ |
|---|
| 1381 |
version (Windows) |
|---|
| 1382 |
{ |
|---|
| 1383 |
void putcw(dchar c) |
|---|
| 1384 |
{ |
|---|
| 1385 |
assert(isValidDchar(c)); |
|---|
| 1386 |
if (c <= 0xFFFF) |
|---|
| 1387 |
{ |
|---|
| 1388 |
FPUTWC(c, fp); |
|---|
| 1389 |
} |
|---|
| 1390 |
else |
|---|
| 1391 |
{ |
|---|
| 1392 |
FPUTWC(cast(wchar) ((((c - 0x10000) >> 10) & 0x3FF) + |
|---|
| 1393 |
0xD800), fp); |
|---|
| 1394 |
FPUTWC(cast(wchar) (((c - 0x10000) & 0x3FF) + 0xDC00), fp); |
|---|
| 1395 |
} |
|---|
| 1396 |
} |
|---|
| 1397 |
} |
|---|
| 1398 |
else version (Posix) |
|---|
| 1399 |
{ |
|---|
| 1400 |
void putcw(dchar c) |
|---|
| 1401 |
{ |
|---|
| 1402 |
FPUTWC(c, fp); |
|---|
| 1403 |
} |
|---|
| 1404 |
} |
|---|
| 1405 |
else |
|---|
| 1406 |
{ |
|---|
| 1407 |
static assert(0); |
|---|
| 1408 |
} |
|---|
| 1409 |
|
|---|
| 1410 |
std.format.doFormat(&putcw, arguments, argptr); |
|---|
| 1411 |
if (newline) |
|---|
| 1412 |
FPUTWC('\n', fp); |
|---|
| 1413 |
} |
|---|
| 1414 |
} |
|---|
| 1415 |
|
|---|
| 1416 |
template isStreamingDevice(T) |
|---|
| 1417 |
{ |
|---|
| 1418 |
enum isStreamingDevice = is(T : FILE*) || |
|---|
| 1419 |
is(T : File); |
|---|
| 1420 |
} |
|---|
| 1421 |
|
|---|
| 1422 |
/*********************************** |
|---|
| 1423 |
For each argument $(D arg) in $(D args), format the argument (as per |
|---|
| 1424 |
$(LINK2 std_conv.html, to!(string)(arg))) and write the resulting |
|---|
| 1425 |
string to $(D args[0]). A call without any arguments will fail to |
|---|
| 1426 |
compile. |
|---|
| 1427 |
|
|---|
| 1428 |
Throws: In case of an I/O error, throws an $(D StdioException). |
|---|
| 1429 |
*/ |
|---|
| 1430 |
void write(T...)(T args) if (!is(T[0] : File)) |
|---|
| 1431 |
{ |
|---|
| 1432 |
stdout.write(args); |
|---|
| 1433 |
} |
|---|
| 1434 |
|
|---|
| 1435 |
unittest |
|---|
| 1436 |
{ |
|---|
| 1437 |
//printf("Entering test at line %d\n", __LINE__); |
|---|
| 1438 |
scope(failure) printf("Failed test at line %d\n", __LINE__); |
|---|
| 1439 |
void[] buf; |
|---|
| 1440 |
write(buf); |
|---|
| 1441 |
// test write |
|---|
| 1442 |
string file = "dmd-build-test.deleteme.txt"; |
|---|
| 1443 |
auto f = File(file, "w"); |
|---|
| 1444 |
// scope(exit) { std.file.remove(file); } |
|---|
| 1445 |
f.write("Hello, ", "world number ", 42, "!"); |
|---|
| 1446 |
f.close; |
|---|
| 1447 |
assert(cast(char[]) std.file.read(file) == "Hello, world number 42!"); |
|---|
| 1448 |
// // test write on stdout |
|---|
| 1449 |
//auto saveStdout = stdout; |
|---|
| 1450 |
//scope(exit) stdout = saveStdout; |
|---|
| 1451 |
//stdout.open(file, "w"); |
|---|
| 1452 |
Object obj; |
|---|
| 1453 |
//write("Hello, ", "world number ", 42, "! ", obj); |
|---|
| 1454 |
//stdout.close; |
|---|
| 1455 |
// auto result = cast(char[]) std.file.read(file); |
|---|
| 1456 |
// assert(result == "Hello, world number 42! null", result); |
|---|
| 1457 |
} |
|---|
| 1458 |
|
|---|
| 1459 |
/*********************************** |
|---|
| 1460 |
* Equivalent to $(D write(args, '\n')). Calling $(D writeln) without |
|---|
| 1461 |
* arguments is valid and just prints a newline to the standard |
|---|
| 1462 |
* output. |
|---|
| 1463 |
*/ |
|---|
| 1464 |
void writeln(T...)(T args) if (T.length == 0) |
|---|
| 1465 |
{ |
|---|
| 1466 |
enforce(fputc('\n', .stdout.p.handle) == '\n'); |
|---|
| 1467 |
} |
|---|
| 1468 |
|
|---|
| 1469 |
unittest |
|---|
| 1470 |
{ |
|---|
| 1471 |
// Just make sure the call compiles |
|---|
| 1472 |
if (false) writeln(); |
|---|
| 1473 |
} |
|---|
| 1474 |
|
|---|
| 1475 |
/// ditto |
|---|
| 1476 |
void writeln(T...)(T args) |
|---|
| 1477 |
if (T.length == 1 && is(typeof(args[0]) : const(char)[])) |
|---|
| 1478 |
{ |
|---|
| 1479 |
enforce(fprintf(.stdout.p.handle, "%.*s\n", |
|---|
| 1480 |
cast(int) args[0].length, args[0].ptr) >= 0); |
|---|
| 1481 |
} |
|---|
| 1482 |
|
|---|
| 1483 |
unittest |
|---|
| 1484 |
{ |
|---|
| 1485 |
if (false) writeln("wyda"); |
|---|
| 1486 |
} |
|---|
| 1487 |
|
|---|
| 1488 |
/// Ditto |
|---|
| 1489 |
void writeln(T...)(T args) |
|---|
| 1490 |
if (T.length > 1 || T.length == 1 && !is(typeof(args[0]) : const(char)[])) |
|---|
| 1491 |
{ |
|---|
| 1492 |
stdout.write(args, '\n'); |
|---|
| 1493 |
} |
|---|
| 1494 |
|
|---|
| 1495 |
unittest |
|---|
| 1496 |
{ |
|---|
| 1497 |
//printf("Entering test at line %d\n", __LINE__); |
|---|
| 1498 |
scope(failure) printf("Failed test at line %d\n", __LINE__); |
|---|
| 1499 |
// test writeln |
|---|
| 1500 |
string file = "dmd-build-test.deleteme.txt"; |
|---|
| 1501 |
auto f = File(file, "w"); |
|---|
| 1502 |
scope(exit) { std.file.remove(file); } |
|---|
| 1503 |
f.writeln("Hello, ", "world number ", 42, "!"); |
|---|
| 1504 |
f.close; |
|---|
| 1505 |
version (Windows) |
|---|
| 1506 |
assert(cast(char[]) std.file.read(file) == |
|---|
| 1507 |
"Hello, world number 42!\r\n"); |
|---|
| 1508 |
else |
|---|
| 1509 |
assert(cast(char[]) std.file.read(file) == |
|---|
| 1510 |
"Hello, world number 42!\n"); |
|---|
| 1511 |
// test writeln on stdout |
|---|
| 1512 |
auto saveStdout = stdout; |
|---|
| 1513 |
scope(exit) stdout = saveStdout; |
|---|
| 1514 |
stdout.open(file, "w"); |
|---|
| 1515 |
writeln("Hello, ", "world number ", 42, "!"); |
|---|
| 1516 |
stdout.close; |
|---|
| 1517 |
version (Windows) |
|---|
| 1518 |
assert(cast(char[]) std.file.read(file) == |
|---|
| 1519 |
"Hello, world number 42!\r\n"); |
|---|
| 1520 |
else |
|---|
| 1521 |
assert(cast(char[]) std.file.read(file) == |
|---|
| 1522 |
"Hello, world number 42!\n"); |
|---|
| 1523 |
} |
|---|
| 1524 |
|
|---|
| 1525 |
/*********************************** |
|---|
| 1526 |
* If the first argument $(D args[0]) is a $(D FILE*), use |
|---|
| 1527 |
* $(LINK2 std_format.html#format-string, the format specifier) in |
|---|
| 1528 |
* $(D args[1]) to control the formatting of $(D |
|---|
| 1529 |
* args[2..$]), and write the resulting string to $(D args[0]). |
|---|
| 1530 |
* If $(D arg[0]) is not a $(D FILE*), the call is |
|---|
| 1531 |
* equivalent to $(D writef(stdout, args)). |
|---|
| 1532 |
* |
|---|
| 1533 |
|
|---|
| 1534 |
IMPORTANT: |
|---|
| 1535 |
|
|---|
| 1536 |
New behavior starting with D 2.006: unlike previous versions, |
|---|
| 1537 |
$(D writef) (and also $(D writefln)) only scans its first |
|---|
| 1538 |
string argument for format specifiers, but not subsequent string |
|---|
| 1539 |
arguments. This decision was made because the old behavior made it |
|---|
| 1540 |
unduly hard to simply print string variables that occasionally |
|---|
| 1541 |
embedded percent signs. |
|---|
| 1542 |
|
|---|
| 1543 |
Also new starting with 2.006 is support for positional |
|---|
| 1544 |
parameters with |
|---|
| 1545 |
$(LINK2 http://opengroup.org/onlinepubs/009695399/functions/printf.html, |
|---|
| 1546 |
POSIX) syntax. |
|---|
| 1547 |
|
|---|
| 1548 |
Example: |
|---|
| 1549 |
|
|---|
| 1550 |
------------------------- |
|---|
| 1551 |
writef("Date: %2$s %1$s", "October", 5); // "Date: 5 October" |
|---|
| 1552 |
------------------------ |
|---|
| 1553 |
|
|---|
| 1554 |
The positional and non-positional styles can be mixed in the same |
|---|
| 1555 |
format string. (POSIX leaves this behavior undefined.) The internal |
|---|
| 1556 |
counter for non-positional parameters tracks the popFront parameter after |
|---|
| 1557 |
the largest positional parameter already used. |
|---|
| 1558 |
|
|---|
| 1559 |
New starting with 2.008: raw format specifiers. Using the "%r" |
|---|
| 1560 |
specifier makes $(D writef) simply write the binary |
|---|
| 1561 |
representation of the argument. Use "%-r" to write numbers in little |
|---|
| 1562 |
endian format, "%+r" to write numbers in big endian format, and "%r" |
|---|
| 1563 |
to write numbers in platform-native format. |
|---|
| 1564 |
|
|---|
| 1565 |
*/ |
|---|
| 1566 |
|
|---|
| 1567 |
void writef(T...)(T args) |
|---|
| 1568 |
{ |
|---|
| 1569 |
stdout.writef(args); |
|---|
| 1570 |
} |
|---|
| 1571 |
|
|---|
| 1572 |
unittest |
|---|
| 1573 |
{ |
|---|
| 1574 |
//printf("Entering test at line %d\n", __LINE__); |
|---|
| 1575 |
scope(failure) printf("Failed test at line %d\n", __LINE__); |
|---|
| 1576 |
// test writef |
|---|
| 1577 |
string file = "dmd-build-test.deleteme.txt"; |
|---|
| 1578 |
auto f = File(file, "w"); |
|---|
| 1579 |
scope(exit) { std.file.remove(file); } |
|---|
| 1580 |
f.writef("Hello, %s world number %s!", "nice", 42); |
|---|
| 1581 |
f.close; |
|---|
| 1582 |
assert(cast(char[]) std.file.read(file) == "Hello, nice world number 42!"); |
|---|
| 1583 |
// test write on stdout |
|---|
| 1584 |
auto saveStdout = stdout; |
|---|
| 1585 |
scope(exit) stdout = saveStdout; |
|---|
| 1586 |
stdout.open(file, "w"); |
|---|
| 1587 |
writef("Hello, %s world number %s!", "nice", 42); |
|---|
| 1588 |
stdout.close; |
|---|
| 1589 |
assert(cast(char[]) std.file.read(file) == "Hello, nice world number 42!"); |
|---|
| 1590 |
} |
|---|
| 1591 |
|
|---|
| 1592 |
/*********************************** |
|---|
| 1593 |
* Equivalent to $(D writef(args, '\n')). |
|---|
| 1594 |
*/ |
|---|
| 1595 |
void writefln(T...)(T args) |
|---|
| 1596 |
{ |
|---|
| 1597 |
stdout.writefln(args); |
|---|
| 1598 |
} |
|---|
| 1599 |
|
|---|
| 1600 |
unittest |
|---|
| 1601 |
{ |
|---|
| 1602 |
//printf("Entering test at line %d\n", __LINE__); |
|---|
| 1603 |
scope(failure) printf("Failed test at line %d\n", __LINE__); |
|---|
| 1604 |
// test writefln |
|---|
| 1605 |
string file = "dmd-build-test.deleteme.txt"; |
|---|
| 1606 |
auto f = File(file, "w"); |
|---|
| 1607 |
scope(exit) { std.file.remove(file); } |
|---|
| 1608 |
f.writefln("Hello, %s world number %s!", "nice", 42); |
|---|
| 1609 |
f.close; |
|---|
| 1610 |
version (Windows) |
|---|
| 1611 |
assert(cast(char[]) std.file.read(file) == |
|---|
| 1612 |
"Hello, nice world number 42!\r\n"); |
|---|
| 1613 |
else |
|---|
| 1614 |
assert(cast(char[]) std.file.read(file) == |
|---|
| 1615 |
"Hello, nice world number 42!\n", |
|---|
| 1616 |
cast(char[]) std.file.read(file)); |
|---|
| 1617 |
// test write on stdout |
|---|
| 1618 |
// auto saveStdout = stdout; |
|---|
| 1619 |
// scope(exit) stdout = saveStdout; |
|---|
| 1620 |
// stdout.open(file, "w"); |
|---|
| 1621 |
// assert(stdout.isOpen); |
|---|
| 1622 |
// writefln("Hello, %s world number %s!", "nice", 42); |
|---|
| 1623 |
// foreach (F ; TypeTuple!(ifloat, idouble, ireal)) |
|---|
| 1624 |
// { |
|---|
| 1625 |
// F a = 5i; |
|---|
| 1626 |
// F b = a % 2; |
|---|
| 1627 |
// writeln(b); |
|---|
| 1628 |
// } |
|---|
| 1629 |
// stdout.close; |
|---|
| 1630 |
// auto read = cast(char[]) std.file.read(file); |
|---|
| 1631 |
// version (Windows) |
|---|
| 1632 |
// assert(read == "Hello, nice world number 42!\r\n1\r\n1\r\n1\r\n", read); |
|---|
| 1633 |
// else |
|---|
| 1634 |
// assert(read == "Hello, nice world number 42!\n1\n1\n1\n", "["~read~"]"); |
|---|
| 1635 |
} |
|---|
| 1636 |
|
|---|
| 1637 |
/** |
|---|
| 1638 |
* Formatted read one line from stdin. |
|---|
| 1639 |
*/ |
|---|
| 1640 |
uint readf(A...)(in char[] format, A args) |
|---|
| 1641 |
{ |
|---|
| 1642 |
return stdin.readf(format, args); |
|---|
| 1643 |
} |
|---|
| 1644 |
|
|---|
| 1645 |
unittest |
|---|
| 1646 |
{ |
|---|
| 1647 |
float f; |
|---|
| 1648 |
if (false) uint x = readf("%s", &f); |
|---|
| 1649 |
} |
|---|
| 1650 |
|
|---|
| 1651 |
/********************************** |
|---|
| 1652 |
* Read line from stream $(D fp). |
|---|
| 1653 |
* Returns: |
|---|
| 1654 |
* $(D null) for end of file, |
|---|
| 1655 |
* $(D char[]) for line read from $(D fp), including terminating character |
|---|
| 1656 |
* Params: |
|---|
| 1657 |
* $(D fp) = input stream |
|---|
| 1658 |
* $(D terminator) = line terminator, '\n' by default |
|---|
| 1659 |
* Throws: |
|---|
| 1660 |
* $(D StdioException) on error |
|---|
| 1661 |
* Example: |
|---|
| 1662 |
* Reads $(D stdin) and writes it to $(D stdout). |
|---|
| 1663 |
--- |
|---|
| 1664 |
import std.stdio; |
|---|
| 1665 |
|
|---|
| 1666 |
int main() |
|---|
| 1667 |
{ |
|---|
| 1668 |
char[] buf; |
|---|
| 1669 |
while ((buf = readln()) != null) |
|---|
| 1670 |
write(buf); |
|---|
| 1671 |
return 0; |
|---|
| 1672 |
} |
|---|
| 1673 |
--- |
|---|
| 1674 |
*/ |
|---|
| 1675 |
string readln(dchar terminator = '\n') |
|---|
| 1676 |
{ |
|---|
| 1677 |
return stdin.readln(terminator); |
|---|
| 1678 |
} |
|---|
| 1679 |
|
|---|
| 1680 |
/** ditto */ |
|---|
| 1681 |
size_t readln(ref char[] buf, dchar terminator = '\n') |
|---|
| 1682 |
{ |
|---|
| 1683 |
return stdin.readln(buf, terminator); |
|---|
| 1684 |
} |
|---|
| 1685 |
|
|---|
| 1686 |
/* |
|---|
| 1687 |
* Convenience function that forwards to $(D std.c.stdio.fopen) |
|---|
| 1688 |
* with appropriately-constructed C-style strings. |
|---|
| 1689 |
*/ |
|---|
| 1690 |
private FILE* fopen(in char[] name, in char[] mode = "r") |
|---|
| 1691 |
{ |
|---|
| 1692 |
const namez = toStringz(name), modez = toStringz(mode); |
|---|
| 1693 |
return fopen64(namez, modez); |
|---|
| 1694 |
} |
|---|
| 1695 |
|
|---|
| 1696 |
version (Posix) |
|---|
| 1697 |
{ |
|---|
| 1698 |
extern(C) FILE* popen(const char*, const char*); |
|---|
| 1699 |
|
|---|
| 1700 |
/*********************************** |
|---|
| 1701 |
* Convenience function that forwards to $(D std.c.stdio.popen) |
|---|
| 1702 |
* with appropriately-constructed C-style strings. |
|---|
| 1703 |
*/ |
|---|
| 1704 |
FILE* popen(in char[] name, in char[] mode = "r") |
|---|
| 1705 |
{ |
|---|
| 1706 |
return popen(toStringz(name), toStringz(mode)); |
|---|
| 1707 |
} |
|---|
| 1708 |
} |
|---|
| 1709 |
|
|---|
| 1710 |
/* |
|---|
| 1711 |
* Convenience function that forwards to $(D std.c.stdio.fwrite) |
|---|
| 1712 |
* and throws an exception upon error |
|---|
| 1713 |
*/ |
|---|
| 1714 |
private void binaryWrite(T)(FILE* f, T obj) |
|---|
| 1715 |
{ |
|---|
| 1716 |
immutable result = fwrite(obj.ptr, obj[0].sizeof, obj.length, f); |
|---|
| 1717 |
if (result != obj.length) StdioException(); |
|---|
| 1718 |
} |
|---|
| 1719 |
|
|---|
| 1720 |
/** |
|---|
| 1721 |
* Iterates through the lines of a file by using $(D foreach). |
|---|
| 1722 |
* |
|---|
| 1723 |
* Example: |
|---|
| 1724 |
* |
|---|
| 1725 |
--------- |
|---|
| 1726 |
void main() |
|---|
| 1727 |
{ |
|---|
| 1728 |
foreach (string line; lines(stdin)) |
|---|
| 1729 |
{ |
|---|
| 1730 |
... use line ... |
|---|
| 1731 |
} |
|---|
| 1732 |
} |
|---|
| 1733 |
--------- |
|---|
| 1734 |
The line terminator ('\n' by default) is part of the string read (it |
|---|
| 1735 |
could be missing in the last line of the file). Several types are |
|---|
| 1736 |
supported for $(D line), and the behavior of $(D lines) |
|---|
| 1737 |
changes accordingly: |
|---|
| 1738 |
|
|---|
| 1739 |
$(OL $(LI If $(D line) has type $(D string), $(D |
|---|
| 1740 |
wstring), or $(D dstring), a new string of the respective type |
|---|
| 1741 |
is allocated every read.) $(LI If $(D line) has type $(D |
|---|
| 1742 |
char[]), $(D wchar[]), $(D dchar[]), the line's content |
|---|
| 1743 |
will be reused (overwritten) across reads.) $(LI If $(D line) |
|---|
| 1744 |
has type $(D immutable(ubyte)[]), the behavior is similar to |
|---|
| 1745 |
case (1), except that no UTF checking is attempted upon input.) $(LI |
|---|
| 1746 |
If $(D line) has type $(D ubyte[]), the behavior is |
|---|
| 1747 |
similar to case (2), except that no UTF checking is attempted upon |
|---|
| 1748 |
input.)) |
|---|
| 1749 |
|
|---|
| 1750 |
In all cases, a two-symbols versions is also accepted, in which case |
|---|
| 1751 |
the first symbol (of integral type, e.g. $(D ulong) or $(D |
|---|
| 1752 |
uint)) tracks the zero-based number of the current line. |
|---|
| 1753 |
|
|---|
| 1754 |
Example: |
|---|
| 1755 |
---- |
|---|
| 1756 |
foreach (ulong i, string line; lines(stdin)) |
|---|
| 1757 |
{ |
|---|
| 1758 |
... use line ... |
|---|
| 1759 |
} |
|---|
| 1760 |
---- |
|---|
| 1761 |
|
|---|
| 1762 |
In case of an I/O error, an $(D StdioException) is thrown. |
|---|
| 1763 |
*/ |
|---|
| 1764 |
|
|---|
| 1765 |
struct lines |
|---|
| 1766 |
{ |
|---|
| 1767 |
private File f; |
|---|
| 1768 |
private dchar terminator = '\n'; |
|---|
| 1769 |
// private string fileName; // Curretly, no use |
|---|
| 1770 |
|
|---|
| 1771 |
this(File f, dchar terminator = '\n') |
|---|
| 1772 |
{ |
|---|
| 1773 |
this.f = f; |
|---|
| 1774 |
this.terminator = terminator; |
|---|
| 1775 |
} |
|---|
| 1776 |
|
|---|
| 1777 |
// Keep these commented lines for later, when Walter fixes the |
|---|
| 1778 |
// exception model. |
|---|
| 1779 |
|
|---|
| 1780 |
// static lines opCall(string fName, dchar terminator = '\n') |
|---|
| 1781 |
// { |
|---|
| 1782 |
// auto f = enforce(fopen(fName), |
|---|
| 1783 |
// new StdioException("Cannot open file `"~fName~"' for reading")); |
|---|
| 1784 |
// auto result = lines(f, terminator); |
|---|
| 1785 |
// result.fileName = fName; |
|---|
| 1786 |
// return result; |
|---|
| 1787 |
// } |
|---|
| 1788 |
|
|---|
| 1789 |
int opApply(D)(scope D dg) |
|---|
| 1790 |
{ |
|---|
| 1791 |
// scope(exit) { |
|---|
| 1792 |
// if (fileName.length && fclose(f)) |
|---|
| 1793 |
// StdioException("Could not close file `"~fileName~"'"); |
|---|
| 1794 |
// } |
|---|
| 1795 |
alias ParameterTypeTuple!(dg) Parms; |
|---|
| 1796 |
static if (isSomeString!(Parms[$ - 1])) |
|---|
| 1797 |
{ |
|---|
| 1798 |
enum bool duplicate = is(Parms[$ - 1] == string) |
|---|
| 1799 |
|| is(Parms[$ - 1] == wstring) || is(Parms[$ - 1] == dstring); |
|---|
| 1800 |
int result = 0; |
|---|
| 1801 |
static if (is(Parms[$ - 1] : const(char)[])) |
|---|
| 1802 |
alias char C; |
|---|
| 1803 |
else static if (is(Parms[$ - 1] : const(wchar)[])) |
|---|
| 1804 |
alias wchar C; |
|---|
| 1805 |
else static if (is(Parms[$ - 1] : const(dchar)[])) |
|---|
| 1806 |
alias dchar C; |
|---|
| 1807 |
C[] line; |
|---|
| 1808 |
static if (Parms.length == 2) |
|---|
| 1809 |
Parms[0] i = 0; |
|---|
| 1810 |
for (;;) |
|---|
| 1811 |
{ |
|---|
| 1812 |
if (!f.readln(line, terminator)) break; |
|---|
| 1813 |
auto copy = to!(Parms[$ - 1])(line); |
|---|
| 1814 |
static if (Parms.length == 2) |
|---|
| 1815 |
{ |
|---|
| 1816 |
result = dg(i, copy); |
|---|
| 1817 |
++i; |
|---|
| 1818 |
} |
|---|
| 1819 |
else |
|---|
| 1820 |
{ |
|---|
| 1821 |
result = dg(copy); |
|---|
| 1822 |
} |
|---|
| 1823 |
if (result != 0) break; |
|---|
| 1824 |
} |
|---|
| 1825 |
return result; |
|---|
| 1826 |
} |
|---|
| 1827 |
else |
|---|
| 1828 |
{ |
|---|
| 1829 |
// raw read |
|---|
| 1830 |
return opApplyRaw(dg); |
|---|
| 1831 |
} |
|---|
| 1832 |
} |
|---|
| 1833 |
// no UTF checking |
|---|
| 1834 |
int opApplyRaw(D)(scope D dg) |
|---|
| 1835 |
{ |
|---|
| 1836 |
alias ParameterTypeTuple!(dg) Parms; |
|---|
| 1837 |
enum duplicate = is(Parms[$ - 1] : immutable(ubyte)[]); |
|---|
| 1838 |
int result = 1; |
|---|
| 1839 |
int c = void; |
|---|
| 1840 |
FLOCK(f.p.handle); |
|---|
| 1841 |
scope(exit) FUNLOCK(f.p.handle); |
|---|
| 1842 |
ubyte[] buffer; |
|---|
| 1843 |
static if (Parms.length == 2) |
|---|
| 1844 |
Parms[0] line = 0; |
|---|
| 1845 |
while ((c = FGETC(cast(_iobuf*)f.p.handle)) != -1) |
|---|
| 1846 |
{ |
|---|
| 1847 |
buffer ~= to!(ubyte)(c); |
|---|
| 1848 |
if (c == terminator) |
|---|
| 1849 |
{ |
|---|
| 1850 |
static if (duplicate) |
|---|
| 1851 |
auto arg = assumeUnique(buffer); |
|---|
| 1852 |
else |
|---|
| 1853 |
alias buffer arg; |
|---|
| 1854 |
// unlock the file while calling the delegate |
|---|
| 1855 |
FUNLOCK(f.p.handle); |
|---|
| 1856 |
scope(exit) FLOCK(f.p.handle); |
|---|
| 1857 |
static if (Parms.length == 1) |
|---|
| 1858 |
{ |
|---|
| 1859 |
result = dg(arg); |
|---|
| 1860 |
} |
|---|
| 1861 |
else |
|---|
| 1862 |
{ |
|---|
| 1863 |
result = dg(line, arg); |
|---|
| 1864 |
++line; |
|---|
| 1865 |
} |
|---|
| 1866 |
if (result) break; |
|---|
| 1867 |
static if (!duplicate) |
|---|
| 1868 |
buffer.length = 0; |
|---|
| 1869 |
} |
|---|
| 1870 |
} |
|---|
| 1871 |
// can only reach when FGETC returned -1 |
|---|
| 1872 |
if (!f.eof) throw new StdioException("Error in reading file"); // error occured |
|---|
| 1873 |
return result; |
|---|
| 1874 |
} |
|---|
| 1875 |
} |
|---|
| 1876 |
|
|---|
| 1877 |
unittest |
|---|
| 1878 |
{ |
|---|
| 1879 |
//printf("Entering test at line %d\n", __LINE__); |
|---|
| 1880 |
scope(failure) printf("Failed test at line %d\n", __LINE__); |
|---|
| 1881 |
string file = "dmd-build-test.deleteme.txt"; |
|---|
| 1882 |
scope(exit) { std.file.remove(file); } |
|---|
| 1883 |
alias TypeTuple!(string, wstring, dstring, |
|---|
| 1884 |
char[], wchar[], dchar[]) |
|---|
| 1885 |
TestedWith; |
|---|
| 1886 |
foreach (T; TestedWith) { |
|---|
| 1887 |
// test looping with an empty file |
|---|
| 1888 |
std.file.write(file, ""); |
|---|
| 1889 |
auto f = File(file, "r"); |
|---|
| 1890 |
foreach (T line; lines(f)) |
|---|
| 1891 |
{ |
|---|
| 1892 |
assert(false); |
|---|
| 1893 |
} |
|---|
| 1894 |
f.close; |
|---|
| 1895 |
|
|---|
| 1896 |
// test looping with a file with three lines |
|---|
| 1897 |
std.file.write(file, "Line one\nline two\nline three\n"); |
|---|
| 1898 |
f.open(file, "r"); |
|---|
| 1899 |
uint i = 0; |
|---|
| 1900 |
foreach (T line; lines(f)) |
|---|
| 1901 |
{ |
|---|
| 1902 |
if (i == 0) assert(line == "Line one\n"); |
|---|
| 1903 |
else if (i == 1) assert(line == "line two\n"); |
|---|
| 1904 |
else if (i == 2) assert(line == "line three\n"); |
|---|
| 1905 |
else assert(false); |
|---|
| 1906 |
++i; |
|---|
| 1907 |
} |
|---|
| 1908 |
f.close; |
|---|
| 1909 |
|
|---|
| 1910 |
// test looping with a file with three lines, last without a newline |
|---|
| 1911 |
std.file.write(file, "Line one\nline two\nline three"); |
|---|
| 1912 |
f.open(file, "r"); |
|---|
| 1913 |
i = 0; |
|---|
| 1914 |
foreach (T line; lines(f)) |
|---|
| 1915 |
{ |
|---|
| 1916 |
if (i == 0) assert(line == "Line one\n"); |
|---|
| 1917 |
else if (i == 1) assert(line == "line two\n"); |
|---|
| 1918 |
else if (i == 2) assert(line == "line three"); |
|---|
| 1919 |
else assert(false); |
|---|
| 1920 |
++i; |
|---|
| 1921 |
} |
|---|
| 1922 |
f.close; |
|---|
| 1923 |
} |
|---|
| 1924 |
|
|---|
| 1925 |
// test with ubyte[] inputs |
|---|
| 1926 |
//@@@BUG 2612@@@ |
|---|
| 1927 |
//alias TypeTuple!(immutable(ubyte)[], ubyte[]) TestedWith2; |
|---|
| 1928 |
alias TypeTuple!(immutable(ubyte)[], ubyte[]) TestedWith2; |
|---|
| 1929 |
foreach (T; TestedWith2) { |
|---|
| 1930 |
// test looping with an empty file |
|---|
| 1931 |
std.file.write(file, ""); |
|---|
| 1932 |
auto f = File(file, "r"); |
|---|
| 1933 |
foreach (T line; lines(f)) |
|---|
| 1934 |
{ |
|---|
| 1935 |
assert(false); |
|---|
| 1936 |
} |
|---|
| 1937 |
f.close; |
|---|
| 1938 |
|
|---|
| 1939 |
// test looping with a file with three lines |
|---|
| 1940 |
std.file.write(file, "Line one\nline two\nline three\n"); |
|---|
| 1941 |
f.open(file, "r"); |
|---|
| 1942 |
uint i = 0; |
|---|
| 1943 |
foreach (T line; lines(f)) |
|---|
| 1944 |
{ |
|---|
| 1945 |
if (i == 0) assert(cast(char[]) line == "Line one\n"); |
|---|
| 1946 |
else if (i == 1) assert(cast(char[]) line == "line two\n", |
|---|
| 1947 |
T.stringof ~ " " ~ cast(char[]) line); |
|---|
| 1948 |
else if (i == 2) assert(cast(char[]) line == "line three\n"); |
|---|
| 1949 |
else assert(false); |
|---|
| 1950 |
++i; |
|---|
| 1951 |
} |
|---|
| 1952 |
f.close; |
|---|
| 1953 |
|
|---|
| 1954 |
// test looping with a file with three lines, last without a newline |
|---|
| 1955 |
std.file.write(file, "Line one\nline two\nline three"); |
|---|
| 1956 |
f.open(file, "r"); |
|---|
| 1957 |
i = 0; |
|---|
| 1958 |
foreach (T line; lines(f)) |
|---|
| 1959 |
{ |
|---|
| 1960 |
if (i == 0) assert(cast(char[]) line == "Line one\n"); |
|---|
| 1961 |
else if (i == 1) assert(cast(char[]) line == "line two\n"); |
|---|
| 1962 |
else if (i == 2) assert(cast(char[]) line == "line three"); |
|---|
| 1963 |
else assert(false); |
|---|
| 1964 |
++i; |
|---|
| 1965 |
} |
|---|
| 1966 |
f.close; |
|---|
| 1967 |
|
|---|
| 1968 |
} |
|---|
| 1969 |
|
|---|
| 1970 |
foreach (T; TypeTuple!(ubyte[])) |
|---|
| 1971 |
{ |
|---|
| 1972 |
// test looping with a file with three lines, last without a newline |
|---|
| 1973 |
// using a counter too this time |
|---|
| 1974 |
std.file.write(file, "Line one\nline two\nline three"); |
|---|
| 1975 |
auto f = File(file, "r"); |
|---|
| 1976 |
uint i = 0; |
|---|
| 1977 |
foreach (ulong j, T line; lines(f)) |
|---|
| 1978 |
{ |
|---|
| 1979 |
if (i == 0) assert(cast(char[]) line == "Line one\n"); |
|---|
| 1980 |
else if (i == 1) assert(cast(char[]) line == "line two\n"); |
|---|
| 1981 |
else if (i == 2) assert(cast(char[]) line == "line three"); |
|---|
| 1982 |
else assert(false); |
|---|
| 1983 |
++i; |
|---|
| 1984 |
} |
|---|
| 1985 |
f.close; |
|---|
| 1986 |
} |
|---|
| 1987 |
} |
|---|
| 1988 |
|
|---|
| 1989 |
/** |
|---|
| 1990 |
Iterates through a file a chunk at a time by using $(D |
|---|
| 1991 |
foreach). |
|---|
| 1992 |
|
|---|
| 1993 |
Example: |
|---|
| 1994 |
|
|---|
| 1995 |
--------- |
|---|
| 1996 |
void main() |
|---|
| 1997 |
{ |
|---|
| 1998 |
foreach (ubyte[] buffer; chunks(stdin, 4096)) |
|---|
| 1999 |
{ |
|---|
| 2000 |
... use buffer ... |
|---|
| 2001 |
} |
|---|
| 2002 |
} |
|---|
| 2003 |
--------- |
|---|
| 2004 |
|
|---|
| 2005 |
The content of $(D buffer) is reused across calls. In the |
|---|
| 2006 |
example above, $(D buffer.length) is 4096 for all iterations, |
|---|
| 2007 |
except for the last one, in which case $(D buffer.length) may |
|---|
| 2008 |
be less than 4096 (but always greater than zero). |
|---|
| 2009 |
|
|---|
| 2010 |
In case of an I/O error, an $(D StdioException) is thrown. |
|---|
| 2011 |
*/ |
|---|
| 2012 |
|
|---|
| 2013 |
struct chunks |
|---|
| 2014 |
{ |
|---|
| 2015 |
private File f; |
|---|
| 2016 |
private size_t size; |
|---|
| 2017 |
// private string fileName; // Currently, no use |
|---|
| 2018 |
|
|---|
| 2019 |
this(File f, size_t size) |
|---|
| 2020 |
in |
|---|
| 2021 |
{ |
|---|
| 2022 |
assert(size, "size must be larger than 0"); |
|---|
| 2023 |
} |
|---|
| 2024 |
body |
|---|
| 2025 |
{ |
|---|
| 2026 |
this.f = f; |
|---|
| 2027 |
this.size = size; |
|---|
| 2028 |
} |
|---|
| 2029 |
|
|---|
| 2030 |
// static chunks opCall(string fName, size_t size) |
|---|
| 2031 |
// { |
|---|
| 2032 |
// auto f = enforce(fopen(fName), |
|---|
| 2033 |
// new StdioException("Cannot open file `"~fName~"' for reading")); |
|---|
| 2034 |
// auto result = chunks(f, size); |
|---|
| 2035 |
// result.fileName = fName; |
|---|
| 2036 |
// return result; |
|---|
| 2037 |
// } |
|---|
| 2038 |
|
|---|
| 2039 |
int opApply(D)(scope D dg) |
|---|
| 2040 |
{ |
|---|
| 2041 |
const maxStackSize = 1024 * 16; |
|---|
| 2042 |
ubyte[] buffer = void; |
|---|
| 2043 |
if (size < maxStackSize) |
|---|
| 2044 |
buffer = (cast(ubyte*) alloca(size))[0 .. size]; |
|---|
| 2045 |
else |
|---|
| 2046 |
buffer = new ubyte[size]; |
|---|
| 2047 |
size_t r = void; |
|---|
| 2048 |
int result = 1; |
|---|
| 2049 |
uint tally = 0; |
|---|
| 2050 |
while ((r = core.stdc.stdio.fread(buffer.ptr, |
|---|
| 2051 |
buffer[0].sizeof, size, f.p.handle)) > 0) |
|---|
| 2052 |
{ |
|---|
| 2053 |
assert(r <= size); |
|---|
| 2054 |
if (r != size) |
|---|
| 2055 |
{ |
|---|
| 2056 |
// error occured |
|---|
| 2057 |
if (!f.eof) throw new StdioException(null); |
|---|
| 2058 |
buffer.length = r; |
|---|
| 2059 |
} |
|---|
| 2060 |
static if (is(typeof(dg(tally, buffer)))) { |
|---|
| 2061 |
if ((result = dg(tally, buffer)) != 0) break; |
|---|
| 2062 |
} else { |
|---|
| 2063 |
if ((result = dg(buffer)) != 0) break; |
|---|
| 2064 |
} |
|---|
| 2065 |
++tally; |
|---|
| 2066 |
} |
|---|
| 2067 |
return result; |
|---|
| 2068 |
} |
|---|
| 2069 |
} |
|---|
| 2070 |
|
|---|
| 2071 |
unittest |
|---|
| 2072 |
{ |
|---|
| 2073 |
//printf("Entering test at line %d\n", __LINE__); |
|---|
| 2074 |
scope(failure) printf("Failed test at line %d\n", __LINE__); |
|---|
| 2075 |
string file = "dmd-build-test.deleteme.txt"; |
|---|
| 2076 |
scope(exit) { std.file.remove(file); } |
|---|
| 2077 |
// test looping with an empty file |
|---|
| 2078 |
std.file.write(file, ""); |
|---|
| 2079 |
auto f = File(file, "r"); |
|---|
| 2080 |
foreach (ubyte[] line; chunks(f, 4)) |
|---|
| 2081 |
{ |
|---|
| 2082 |
assert(false); |
|---|
| 2083 |
} |
|---|
| 2084 |
f.close; |
|---|
| 2085 |
|
|---|
| 2086 |
// test looping with a file with three lines |
|---|
| 2087 |
std.file.write(file, "Line one\nline two\nline three\n"); |
|---|
| 2088 |
f = File(file, "r"); |
|---|
| 2089 |
uint i = 0; |
|---|
| 2090 |
foreach (ubyte[] line; chunks(f, 3)) |
|---|
| 2091 |
{ |
|---|
| 2092 |
if (i == 0) assert(cast(char[]) line == "Lin"); |
|---|
| 2093 |
else if (i == 1) assert(cast(char[]) line == "e o"); |
|---|
| 2094 |
else if (i == 2) assert(cast(char[]) line == "ne\n"); |
|---|
| 2095 |
else break; |
|---|
| 2096 |
++i; |
|---|
| 2097 |
} |
|---|
| 2098 |
f.close; |
|---|
| 2099 |
} |
|---|
| 2100 |
|
|---|
| 2101 |
/********************* |
|---|
| 2102 |
* Thrown if I/O errors happen. |
|---|
| 2103 |
*/ |
|---|
| 2104 |
class StdioException : Exception |
|---|
| 2105 |
{ |
|---|
| 2106 |
/// Operating system error code. |
|---|
| 2107 |
uint errno; |
|---|
| 2108 |
|
|---|
| 2109 |
/** |
|---|
| 2110 |
Initialize with a message and an error code. */ |
|---|
| 2111 |
this(string message, uint e = .getErrno) |
|---|
| 2112 |
{ |
|---|
| 2113 |
errno = e; |
|---|
| 2114 |
version (Posix) |
|---|
| 2115 |
{ |
|---|
| 2116 |
char[256] buf = void; |
|---|
| 2117 |
version (linux) |
|---|
| 2118 |
{ |
|---|
| 2119 |
auto s = std.c.string.strerror_r(errno, buf.ptr, buf.length); |
|---|
| 2120 |
} |
|---|
| 2121 |
else |
|---|
| 2122 |
{ |
|---|
| 2123 |
std.c.string.strerror_r(errno, buf.ptr, buf.length); |
|---|
| 2124 |
auto s = buf.ptr; |
|---|
| 2125 |
} |
|---|
| 2126 |
} |
|---|
| 2127 |
else |
|---|
| 2128 |
{ |
|---|
| 2129 |
auto s = std.c.string.strerror(errno); |
|---|
| 2130 |
} |
|---|
| 2131 |
auto sysmsg = to!string(s); |
|---|
| 2132 |
super(message ? message ~ "(" ~ sysmsg ~ ")" : sysmsg); |
|---|
| 2133 |
} |
|---|
| 2134 |
|
|---|
| 2135 |
/** Convenience functions that throw an $(D StdioException). */ |
|---|
| 2136 |
static void opCall(string msg) |
|---|
| 2137 |
{ |
|---|
| 2138 |
throw new StdioException(msg); |
|---|
| 2139 |
} |
|---|
| 2140 |
|
|---|
| 2141 |
/// ditto |
|---|
| 2142 |
static void opCall() |
|---|
| 2143 |
{ |
|---|
| 2144 |
throw new StdioException(null, .getErrno); |
|---|
| 2145 |
} |
|---|
| 2146 |
} |
|---|
| 2147 |
|
|---|
| 2148 |
extern(C) void std_stdio_static_this() |
|---|
| 2149 |
{ |
|---|
| 2150 |
//Bind stdin, stdout, stderr |
|---|
| 2151 |
__gshared File.Impl stdinImpl; |
|---|
| 2152 |
stdinImpl.handle = core.stdc.stdio.stdin; |
|---|
| 2153 |
.stdin.p = &stdinImpl; |
|---|
| 2154 |
// stdout |
|---|
| 2155 |
__gshared File.Impl stdoutImpl; |
|---|
| 2156 |
stdoutImpl.handle = core.stdc.stdio.stdout; |
|---|
| 2157 |
.stdout.p = &stdoutImpl; |
|---|
| 2158 |
// stderr |
|---|
| 2159 |
__gshared File.Impl stderrImpl; |
|---|
| 2160 |
stderrImpl.handle = core.stdc.stdio.stderr; |
|---|
| 2161 |
.stderr.p = &stderrImpl; |
|---|
| 2162 |
} |
|---|
| 2163 |
|
|---|
| 2164 |
//--------- |
|---|
| 2165 |
__gshared |
|---|
| 2166 |
{ |
|---|
| 2167 |
File stdin; |
|---|
| 2168 |
File stdout; |
|---|
| 2169 |
File stderr; |
|---|
| 2170 |
} |
|---|
| 2171 |
|
|---|
| 2172 |
unittest |
|---|
| 2173 |
{ |
|---|
| 2174 |
scope(failure) printf("Failed test at line %d\n", __LINE__); |
|---|
| 2175 |
std.file.write("deleteme", "1 2\n4 1\n5 100"); |
|---|
| 2176 |
scope(exit) std.file.remove("deleteme"); |
|---|
| 2177 |
{ |
|---|
| 2178 |
File f = File("deleteme"); |
|---|
| 2179 |
scope(exit) f.close; |
|---|
| 2180 |
auto t = [ tuple(1, 2), tuple(4, 1), tuple(5, 100) ]; |
|---|
| 2181 |
uint i; |
|---|
| 2182 |
foreach (e; f.byRecord!(int, int)("%s %s")) |
|---|
| 2183 |
{ |
|---|
| 2184 |
//writeln(e); |
|---|
| 2185 |
assert(e == t[i++]); |
|---|
| 2186 |
} |
|---|
| 2187 |
assert(i == 3); |
|---|
| 2188 |
} |
|---|
| 2189 |
} |
|---|
| 2190 |
|
|---|
| 2191 |
// Private implementation of readln |
|---|
| 2192 |
version (DIGITAL_MARS_STDIO) |
|---|
| 2193 |
private size_t readlnImpl(FILE* fps, ref char[] buf, dchar terminator = '\n') |
|---|
| 2194 |
{ |
|---|
| 2195 |
FLOCK(fps); |
|---|
| 2196 |
scope(exit) FUNLOCK(fps); |
|---|
| 2197 |
|
|---|
| 2198 |
/* Since fps is now locked, we can create an "unshared" version |
|---|
| 2199 |
* of fp. |
|---|
| 2200 |
*/ |
|---|
| 2201 |
auto fp = cast(_iobuf*)fps; |
|---|
| 2202 |
|
|---|
| 2203 |
if (__fhnd_info[fp._file] & FHND_WCHAR) |
|---|
| 2204 |
{ /* Stream is in wide characters. |
|---|
| 2205 |
* Read them and convert to chars. |
|---|
| 2206 |
*/ |
|---|
| 2207 |
static assert(wchar_t.sizeof == 2); |
|---|
| 2208 |
auto app = appender(buf); |
|---|
| 2209 |
app.clear(); |
|---|
| 2210 |
for (int c = void; (c = FGETWC(fp)) != -1; ) |
|---|
| 2211 |
{ |
|---|
| 2212 |
if ((c & ~0x7F) == 0) |
|---|
| 2213 |
{ app.put(cast(char) c); |
|---|
| 2214 |
if (c == terminator) |
|---|
| 2215 |
break; |
|---|
| 2216 |
} |
|---|
| 2217 |
else |
|---|
| 2218 |
{ |
|---|
| 2219 |
if (c >= 0xD800 && c <= 0xDBFF) |
|---|
| 2220 |
{ |
|---|
| 2221 |
int c2 = void; |
|---|
| 2222 |
if ((c2 = FGETWC(fp)) != -1 || |
|---|
| 2223 |
c2 < 0xDC00 && c2 > 0xDFFF) |
|---|
| 2224 |
{ |
|---|
| 2225 |
StdioException("unpaired UTF-16 surrogate"); |
|---|
| 2226 |
} |
|---|
| 2227 |
c = ((c - 0xD7C0) << 10) + (c2 - 0xDC00); |
|---|
| 2228 |
} |
|---|
| 2229 |
//std.utf.encode(buf, c); |
|---|
| 2230 |
app.put(cast(dchar)c); |
|---|
| 2231 |
} |
|---|
| 2232 |
} |
|---|
| 2233 |
if (ferror(fps)) |
|---|
| 2234 |
StdioException(); |
|---|
| 2235 |
buf = app.data; |
|---|
| 2236 |
return buf.length; |
|---|
| 2237 |
} |
|---|
| 2238 |
|
|---|
| 2239 |
auto sz = GC.sizeOf(buf.ptr); |
|---|
| 2240 |
//auto sz = buf.length; |
|---|
| 2241 |
buf = buf.ptr[0 .. sz]; |
|---|
| 2242 |
if (fp._flag & _IONBF) |
|---|
| 2243 |
{ |
|---|
| 2244 |
/* Use this for unbuffered I/O, when running |
|---|
| 2245 |
* across buffer boundaries, or for any but the common |
|---|
| 2246 |
* cases. |
|---|
| 2247 |
*/ |
|---|
| 2248 |
L1: |
|---|
| 2249 |
auto app = appender(buf); |
|---|
| 2250 |
app.clear(); |
|---|
| 2251 |
if(app.capacity == 0) |
|---|
| 2252 |
app.reserve(128); // get at least 128 bytes available |
|---|
| 2253 |
|
|---|
| 2254 |
int c; |
|---|
| 2255 |
while((c = FGETC(fp)) != -1) { |
|---|
| 2256 |
app.put(cast(char) c); |
|---|
| 2257 |
if(c == terminator) { |
|---|
| 2258 |
buf = app.data; |
|---|
| 2259 |
return buf.length; |
|---|
| 2260 |
} |
|---|
| 2261 |
|
|---|
| 2262 |
} |
|---|
| 2263 |
|
|---|
| 2264 |
if (ferror(fps)) |
|---|
| 2265 |
StdioException(); |
|---|
| 2266 |
buf = app.data; |
|---|
| 2267 |
return buf.length; |
|---|
| 2268 |
} |
|---|
| 2269 |
else |
|---|
| 2270 |
{ |
|---|
| 2271 |
int u = fp._cnt; |
|---|
| 2272 |
char* p = fp._ptr; |
|---|
| 2273 |
int i; |
|---|
| 2274 |
if (fp._flag & _IOTRAN) |
|---|
| 2275 |
{ /* Translated mode ignores \r and treats ^Z as end-of-file |
|---|
| 2276 |
*/ |
|---|
| 2277 |
char c; |
|---|
| 2278 |
while (1) |
|---|
| 2279 |
{ |
|---|
| 2280 |
if (i == u) // if end of buffer |
|---|
| 2281 |
goto L1; // give up |
|---|
| 2282 |
c = p[i]; |
|---|
| 2283 |
i++; |
|---|
| 2284 |
if (c != '\r') |
|---|
| 2285 |
{ |
|---|
| 2286 |
if (c == terminator) |
|---|
| 2287 |
break; |
|---|
| 2288 |
if (c != 0x1A) |
|---|
| 2289 |
continue; |
|---|
| 2290 |
goto L1; |
|---|
| 2291 |
} |
|---|
| 2292 |
else |
|---|
| 2293 |
{ if (i != u && p[i] == terminator) |
|---|
| 2294 |
break; |
|---|
| 2295 |
goto L1; |
|---|
| 2296 |
} |
|---|
| 2297 |
} |
|---|
| 2298 |
if (i > sz) |
|---|
| 2299 |
{ |
|---|
| 2300 |
buf = cast(char[])GC.malloc(i, GC.BlkAttr.NO_SCAN)[0 .. i]; |
|---|
| 2301 |
} |
|---|
| 2302 |
if (i - 1) |
|---|
| 2303 |
memcpy(buf.ptr, p, i - 1); |
|---|
| 2304 |
buf[i - 1] = cast(char)terminator; |
|---|
| 2305 |
buf = buf[0 .. i]; |
|---|
| 2306 |
if (terminator == '\n' && c == '\r') |
|---|
| 2307 |
i++; |
|---|
| 2308 |
} |
|---|
| 2309 |
else |
|---|
| 2310 |
{ |
|---|
| 2311 |
while (1) |
|---|
| 2312 |
{ |
|---|
| 2313 |
if (i == u) // if end of buffer |
|---|
| 2314 |
goto L1; // give up |
|---|
| 2315 |
auto c = p[i]; |
|---|
| 2316 |
i++; |
|---|
| 2317 |
if (c == terminator) |
|---|
| 2318 |
break; |
|---|
| 2319 |
} |
|---|
| 2320 |
if (i > sz) |
|---|
| 2321 |
{ |
|---|
| 2322 |
buf = cast(char[])GC.malloc(i, GC.BlkAttr.NO_SCAN)[0 .. i]; |
|---|
| 2323 |
} |
|---|
| 2324 |
memcpy(buf.ptr, p, i); |
|---|
| 2325 |
buf = buf[0 .. i]; |
|---|
| 2326 |
} |
|---|
| 2327 |
fp._cnt -= i; |
|---|
| 2328 |
fp._ptr += i; |
|---|
| 2329 |
return i; |
|---|
| 2330 |
} |
|---|
| 2331 |
} |
|---|
| 2332 |
|
|---|
| 2333 |
version (GCC_IO) |
|---|
| 2334 |
private size_t readlnImpl(FILE* fps, ref char[] buf, dchar terminator = '\n') |
|---|
| 2335 |
{ |
|---|
| 2336 |
if (fwide(fps, 0) > 0) |
|---|
| 2337 |
{ /* Stream is in wide characters. |
|---|
| 2338 |
* Read them and convert to chars. |
|---|
| 2339 |
*/ |
|---|
| 2340 |
FLOCK(fps); |
|---|
| 2341 |
scope(exit) FUNLOCK(fps); |
|---|
| 2342 |
auto fp = cast(_iobuf*)fps; |
|---|
| 2343 |
version (Windows) |
|---|
| 2344 |
{ |
|---|
| 2345 |
buf.length = 0; |
|---|
| 2346 |
for (int c = void; (c = FGETWC(fp)) != -1; ) |
|---|
| 2347 |
{ |
|---|
| 2348 |
if ((c & ~0x7F) == 0) |
|---|
| 2349 |
{ buf ~= c; |
|---|
| 2350 |
if (c == terminator) |
|---|
| 2351 |
break; |
|---|
| 2352 |
} |
|---|
| 2353 |
else |
|---|
| 2354 |
{ |
|---|
| 2355 |
if (c >= 0xD800 && c <= 0xDBFF) |
|---|
| 2356 |
{ |
|---|
| 2357 |
int c2 = void; |
|---|
| 2358 |
if ((c2 = FGETWC(fp)) != -1 || |
|---|
| 2359 |
c2 < 0xDC00 && c2 > 0xDFFF) |
|---|
| 2360 |
{ |
|---|
| 2361 |
StdioException("unpaired UTF-16 surrogate"); |
|---|
| 2362 |
} |
|---|
| 2363 |
c = ((c - 0xD7C0) << 10) + (c2 - 0xDC00); |
|---|
| 2364 |
} |
|---|
| 2365 |
std.utf.encode(buf, c); |
|---|
| 2366 |
} |
|---|
| 2367 |
} |
|---|
| 2368 |
if (ferror(fp)) |
|---|
| 2369 |
StdioException(); |
|---|
| 2370 |
return buf.length; |
|---|
| 2371 |
} |
|---|
| 2372 |
else version (Posix) |
|---|
| 2373 |
{ |
|---|
| 2374 |
buf.length = 0; |
|---|
| 2375 |
for (int c; (c = FGETWC(fp)) != -1; ) |
|---|
| 2376 |
{ |
|---|
| 2377 |
if ((c & ~0x7F) == 0) |
|---|
| 2378 |
buf ~= cast(char)c; |
|---|
| 2379 |
else |
|---|
| 2380 |
std.utf.encode(buf, cast(dchar)c); |
|---|
| 2381 |
if (c == terminator) |
|---|
| 2382 |
break; |
|---|
| 2383 |
} |
|---|
| 2384 |
if (ferror(fps)) |
|---|
| 2385 |
StdioException(); |
|---|
| 2386 |
return buf.length; |
|---|
| 2387 |
} |
|---|
| 2388 |
else |
|---|
| 2389 |
{ |
|---|
| 2390 |
static assert(0); |
|---|
| 2391 |
} |
|---|
| 2392 |
} |
|---|
| 2393 |
|
|---|
| 2394 |
char *lineptr = null; |
|---|
| 2395 |
size_t n = 0; |
|---|
| 2396 |
auto s = getdelim(&lineptr, &n, terminator, fps); |
|---|
| 2397 |
scope(exit) free(lineptr); |
|---|
| 2398 |
if (s < 0) |
|---|
| 2399 |
{ |
|---|
| 2400 |
if (ferror(fps)) |
|---|
| 2401 |
StdioException(); |
|---|
| 2402 |
buf.length = 0; // end of file |
|---|
| 2403 |
return 0; |
|---|
| 2404 |
} |
|---|
| 2405 |
buf = buf.ptr[0 .. GC.sizeOf(buf.ptr)]; |
|---|
| 2406 |
if (s <= buf.length) |
|---|
| 2407 |
{ |
|---|
| 2408 |
buf.length = s; |
|---|
| 2409 |
buf[] = lineptr[0 .. s]; |
|---|
| 2410 |
} |
|---|
| 2411 |
else |
|---|
| 2412 |
{ |
|---|
| 2413 |
buf = lineptr[0 .. s].dup; |
|---|
| 2414 |
} |
|---|
| 2415 |
return s; |
|---|
| 2416 |
} |
|---|
| 2417 |
|
|---|
| 2418 |
version (GENERIC_IO) |
|---|
| 2419 |
private size_t readlnImpl(FILE* fps, ref char[] buf, dchar terminator = '\n') |
|---|
| 2420 |
{ |
|---|
| 2421 |
FLOCK(fps); |
|---|
| 2422 |
scope(exit) FUNLOCK(fps); |
|---|
| 2423 |
auto fp = cast(_iobuf*)fps; |
|---|
| 2424 |
if (fwide(fps, 0) > 0) |
|---|
| 2425 |
{ /* Stream is in wide characters. |
|---|
| 2426 |
* Read them and convert to chars. |
|---|
| 2427 |
*/ |
|---|
| 2428 |
version (Windows) |
|---|
| 2429 |
{ |
|---|
| 2430 |
buf.length = 0; |
|---|
| 2431 |
for (int c; (c = FGETWC(fp)) != -1; ) |
|---|
| 2432 |
{ |
|---|
| 2433 |
if ((c & ~0x7F) == 0) |
|---|
| 2434 |
{ buf ~= c; |
|---|
| 2435 |
if (c == terminator) |
|---|
| 2436 |
break; |
|---|
| 2437 |
} |
|---|
| 2438 |
else |
|---|
| 2439 |
{ |
|---|
| 2440 |
if (c >= 0xD800 && c <= 0xDBFF) |
|---|
| 2441 |
{ |
|---|
| 2442 |
int c2 = void; |
|---|
| 2443 |
if ((c2 = FGETWC(fp)) != -1 || |
|---|
| 2444 |
c2 < 0xDC00 && c2 > 0xDFFF) |
|---|
| 2445 |
{ |
|---|
| 2446 |
StdioException("unpaired UTF-16 surrogate"); |
|---|
| 2447 |
} |
|---|
| 2448 |
c = ((c - 0xD7C0) << 10) + (c2 - 0xDC00); |
|---|
| 2449 |
} |
|---|
| 2450 |
std.utf.encode(buf, c); |
|---|
| 2451 |
} |
|---|
| 2452 |
} |
|---|
| 2453 |
if (ferror(fp)) |
|---|
| 2454 |
StdioException(); |
|---|
| 2455 |
return buf.length; |
|---|
| 2456 |
} |
|---|
| 2457 |
else version (Posix) |
|---|
| 2458 |
{ |
|---|
| 2459 |
buf.length = 0; |
|---|
| 2460 |
for (int c; (c = FGETWC(fp)) != -1; ) |
|---|
| 2461 |
{ |
|---|
| 2462 |
if ((c & ~0x7F) == 0) |
|---|
| 2463 |
buf ~= cast(char)c; |
|---|
| 2464 |
else |
|---|
| 2465 |
std.utf.encode(buf, cast(dchar)c); |
|---|
| 2466 |
if (c == terminator) |
|---|
| 2467 |
break; |
|---|
| 2468 |
} |
|---|
| 2469 |
if (ferror(fps)) |
|---|
| 2470 |
StdioException(); |
|---|
| 2471 |
return buf.length; |
|---|
| 2472 |
} |
|---|
| 2473 |
else |
|---|
| 2474 |
{ |
|---|
| 2475 |
static assert(0); |
|---|
| 2476 |
} |
|---|
| 2477 |
} |
|---|
| 2478 |
|
|---|
| 2479 |
// Narrow stream |
|---|
| 2480 |
buf.length = 0; |
|---|
| 2481 |
for (int c; (c = FGETC(fp)) != -1; ) |
|---|
| 2482 |
{ |
|---|
| 2483 |
buf ~= cast(char)c; |
|---|
| 2484 |
if (c == terminator) |
|---|
| 2485 |
break; |
|---|
| 2486 |
} |
|---|
| 2487 |
if (ferror(fps)) |
|---|
| 2488 |
StdioException(); |
|---|
| 2489 |
return buf.length; |
|---|
| 2490 |
} |
|---|
| 2491 |
|
|---|
| 2492 |
|
|---|
| 2493 |
/** Experimental network access via the File interface |
|---|
| 2494 |
|
|---|
| 2495 |
Opens a TCP connection to the given host and port, then returns |
|---|
| 2496 |
a File struct with read and write access through the same interface |
|---|
| 2497 |
as any other file (meaning writef and the byLine ranges work!). |
|---|
| 2498 |
|
|---|
| 2499 |
Authors: |
|---|
| 2500 |
Adam D. Ruppe |
|---|
| 2501 |
|
|---|
| 2502 |
Bugs: |
|---|
| 2503 |
Only works on Linux |
|---|
| 2504 |
*/ |
|---|
| 2505 |
version(linux) { |
|---|
| 2506 |
static import linux = std.c.linux.linux; |
|---|
| 2507 |
static import sock = std.c.linux.socket; |
|---|
| 2508 |
|
|---|
| 2509 |
File openNetwork(string host, ushort port) { |
|---|
| 2510 |
auto h = enforce( sock.gethostbyname(std.string.toStringz(host)), |
|---|
| 2511 |
new StdioException("gethostbyname")); |
|---|
| 2512 |
|
|---|
| 2513 |
int s = sock.socket(sock.AF_INET, sock.SOCK_STREAM, 0); |
|---|
| 2514 |
enforce(s != -1, new StdioException("socket")); |
|---|
| 2515 |
|
|---|
| 2516 |
scope(failure) { |
|---|
| 2517 |
linux.close(s); // want to make sure it doesn't dangle if |
|---|
| 2518 |
// something throws. Upon normal exit, the |
|---|
| 2519 |
// File struct's reference counting takes |
|---|
| 2520 |
// care of closing, so we don't need to |
|---|
| 2521 |
// worry about success |
|---|
| 2522 |
} |
|---|
| 2523 |
|
|---|
| 2524 |
sock.sockaddr_in addr; |
|---|
| 2525 |
|
|---|
| 2526 |
addr.sin_family = sock.AF_INET; |
|---|
| 2527 |
addr.sin_port = sock.htons(port); |
|---|
| 2528 |
std.c.string.memcpy(&addr.sin_addr.s_addr, h.h_addr, h.h_length); |
|---|
| 2529 |
|
|---|
| 2530 |
enforce(sock.connect(s, cast(sock.sockaddr*) &addr, addr.sizeof) != -1, |
|---|
| 2531 |
new StdioException("Connect failed")); |
|---|
| 2532 |
|
|---|
| 2533 |
FILE* fp = enforce(fdopen(s, "w+".ptr)); |
|---|
| 2534 |
|
|---|
| 2535 |
File f; |
|---|
| 2536 |
f.p = new File.Impl(fp, 1, host ~ ":" ~ to!string(port)); |
|---|
| 2537 |
|
|---|
| 2538 |
return f; |
|---|
| 2539 |
} |
|---|
| 2540 |
} |
|---|