Changeset 1721
- Timestamp:
- 07/04/10 21:49:23 (14 years ago)
- Files:
-
- trunk/phobos/std/stdio.d (modified) (9 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/phobos/std/stdio.d
r1624 r1721 1 1 // Written in the D programming language. 2 2 3 3 /** 4 4 Standard I/O functions that extend $(B std.c.stdio). $(B std.c.stdio) 5 5 is $(D_PARAM public)ally imported when importing $(B std.stdio). 6 6 7 7 Macros: 8 8 WIKI=Phobos/StdStdio 9 9 10 Copyright: Copyright Digital Mars 2007 - 2009.11 License: <a href="http://www.boost.org/LICENSE_1_0.txt">Boost License 1.0</a>.10 Copyright: Copyright Digital Mars 2007-. 11 License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). 12 12 Authors: $(WEB digitalmars.com, Walter Bright), 13 13 $(WEB erdani.org, Andrei Alexandrescu) 14 15 Copyright Digital Mars 2007 - 2009.16 Distributed under the Boost Software License, Version 1.0.17 (See accompanying file LICENSE_1_0.txt or copy at18 http://www.boost.org/LICENSE_1_0.txt)19 14 */ 20 15 module std.stdio; 21 16 22 17 public import core.stdc.stdio; 23 privateimport std.stdiobase;24 private import core.memory, core.stdc.errno, core.stdc.stddef,25 core.stdc.st dlib, core.stdc.string, core.stdc.wchar_;26 privateimport std.algorithm, std.array, std.contracts, std.conv, std.file, std.format,27 /*std.metastrings,*/std.range, std.string, std.traits, std.typecons,18 import std.stdiobase; 19 import core.memory, core.stdc.errno, core.stdc.stddef, core.stdc.stdlib, 20 core.stdc.string, core.stdc.wchar_; 21 import std.algorithm, std.array, std.contracts, std.conv, std.file, std.format, 22 std.range, std.string, std.traits, std.typecons, 28 23 std.typetuple, std.utf; 29 24 30 25 version (DigitalMars) version (Windows) 31 26 { 32 27 // Specific to the way Digital Mars C does stdio 33 28 version = DIGITAL_MARS_STDIO; 34 29 import std.c.stdio : __fhnd_info, FHND_WCHAR, FHND_TEXT; 35 30 } 36 31 37 32 version (linux) … … 128 123 } 129 124 else version (GENERIC_IO) 130 125 { 131 126 extern (C) 132 127 { 133 128 void flockfile(FILE*); 134 129 void funlockfile(FILE*); 135 130 } 136 131 137 132 int fputc_unlocked(int c, _iobuf* fp) { return fputc(c, cast(shared) fp); } 138 int fputwc_unlocked(wchar_t c, _iobuf* fp) { return fputwc(c, cast(shared) fp); } 133 int fputwc_unlocked(wchar_t c, _iobuf* fp) 134 { 135 return fputwc(c, cast(shared) fp); 136 } 139 137 int fgetc_unlocked(_iobuf* fp) { return fgetc(cast(shared) fp); } 140 138 int fgetwc_unlocked(_iobuf* fp) { return fgetwc(cast(shared) fp); } 141 139 142 140 alias fputc_unlocked FPUTC; 143 141 alias fputwc_unlocked FPUTWC; 144 142 alias fgetc_unlocked FGETC; 145 143 alias fgetwc_unlocked FGETWC; 146 144 147 145 alias flockfile FLOCK; 148 146 alias funlockfile FUNLOCK; 149 147 } 150 148 else 151 149 { 152 150 static assert(0, "unsupported C I/O system"); 153 151 } 154 152 155 153 //------------------------------------------------------------------------------ 156 154 struct ByRecord(Fields...) 157 155 { 156 private: 158 157 File file; 159 158 char[] line; 160 159 Tuple!(Fields) current; 161 160 string format; 162 161 162 public: 163 163 this(File f, string format) 164 164 { 165 165 assert(f.isOpen); 166 166 file = f; 167 167 this.format = format; 168 168 popFront; // prime the range 169 169 } 170 170 171 171 /// Range primitive implementations. 172 bool empty()172 @property bool empty() 173 173 { 174 174 return !file.isOpen; 175 175 } 176 176 177 177 /// Ditto 178 ref Tuple!(Fields) front()178 @property ref Tuple!(Fields) front() 179 179 { 180 180 return current; 181 181 } 182 182 183 183 /// Ditto 184 184 void popFront() 185 185 { 186 186 enforce(file.isOpen); 187 187 file.readln(line); 188 188 if (!line.length) … … 240 240 ---- 241 241 <pre class=console> 242 242 % rdmd test.d Jimmy 243 243 % cat test.txt 244 244 Hello, Jimmy! 245 245 % __ 246 246 </pre> 247 247 */ 248 248 struct File 249 249 { 250 /*private*/struct Impl250 private struct Impl 251 251 { 252 252 FILE * handle = null; 253 253 uint refs = uint.max / 2; 254 254 string name = null; 255 255 this(FILE* h, uint r, string n) 256 256 { 257 257 handle = h; 258 258 refs = r; 259 259 name = n; 260 260 } 261 261 } 262 /*private*/Impl * p;262 private Impl * p; 263 263 264 264 /** 265 265 Constructor taking the name of the file to open and the open mode 266 266 (with the same semantics as in the C standard library $(WEB 267 267 cplusplus.com/reference/clibrary/cstdio/fopen.html, fopen) 268 268 function). Throws an exception if the file could not be opened. 269 269 270 270 Copying one $(D File) object to another results in the two $(D File) 271 271 objects referring to the same underlying file. 272 272 273 273 The destructor automatically closes the file as soon as no $(D File) 274 274 object refers to it anymore. 275 275 */ 276 277 276 this(string name, in char[] stdioOpenmode = "rb") 278 277 { 279 278 p = new Impl(errnoEnforce(.fopen(name, stdioOpenmode), 280 "Cannot open file `"~name281 ~"' in mode `"~stdioOpenmode.idup~"'"),279 text("Cannot open file `", name, "' in mode `", 280 stdioOpenmode, "'")), 282 281 1, name); 283 282 } 284 283 285 284 ~this() 286 285 { 287 286 if (!p) return; 288 // @@@BUG@@@ These lines prematurely close the file289 //printf("Destroying file `%s' with %d refs\n", toStringz(p.name), p.refs);290 287 if (p.refs == 1) close; 291 288 else --p.refs; 292 289 } 293 290 294 291 this(this) 295 292 { 296 293 if (!p) return; 297 //printf("Copying file %s with %d refs\n", toStringz(p.name), p.refs); 298 enforce(p.refs); 294 assert(p.refs); 299 295 ++p.refs; 300 296 } 301 297 302 298 /** 303 299 Assigns a file to another. The target of the assignment gets detached 304 300 from whatever file it was attached to, and attaches itself to the new 305 301 file. 306 302 */ 307 303 void opAssign(File rhs) 308 304 { 309 // printf("Assigning file %s with %d refs\n",310 // toStringz(rhs.p.name), rhs.p.refs);311 // @@@BUG@@@312 305 swap(p, rhs.p); 313 // p = rhs.p;314 // rhs.p = null;315 306 } 316 307 317 308 /** 318 309 First calls $(D detach) (throwing on failure), and then attempts to 319 310 _open file $(D name) with mode $(D stdioOpenmode). The mode has the 320 311 same semantics as in the C standard library $(WEB 321 312 cplusplus.com/reference/clibrary/cstdio/fopen.html, fopen) function. 322 313 Throws exception in case of error. 323 314 */ 324 void open(string name, stringstdioOpenmode = "rb")315 void open(string name, in char[] stdioOpenmode = "rb") 325 316 { 326 317 detach; 327 318 auto another = File(name, stdioOpenmode); 328 319 swap(this, another); 329 320 } 330 321 331 322 /** 332 323 First calls $(D detach) (throwing on failure), and then runs a command 333 324 by calling the C standard library function $(WEB 334 325 opengroup.org/onlinepubs/007908799/xsh/_popen.html, _popen). 335 326 */ 336 version(Posix) void popen(string command, stringstdioOpenmode = "r")327 version(Posix) void popen(string command, in char[] stdioOpenmode = "r") 337 328 { 338 329 detach; 339 330 p = new Impl(errnoEnforce(.popen(command, stdioOpenmode), 340 331 "Cannot run command `"~command~"'"), 341 332 1, command); 342 333 } 343 334 344 335 /** Returns $(D true) if the file is opened. */ 345 bool isOpen() const336 @property bool isOpen() const 346 337 { 347 338 return p !is null && p.handle; 348 339 } 349 340 350 341 /** 351 342 Returns $(D true) if the file is at end (see $(WEB 352 343 cplusplus.com/reference/clibrary/cstdio/feof.html, feof)). The file 353 344 must be opened, otherwise an exception is thrown. 354 345 */ 355 bool eof() const346 @property bool eof() const 356 347 { 357 348 enforce(p && p.handle, "Calling eof() against an unopened file."); 358 349 return .feof(cast(FILE*) p.handle) != 0; 359 350 } 360 351 361 352 /** Returns the name of the file, if any. */ 362 string name() const353 @property string name() const 363 354 { 364 355 return p.name; 365 356 } 366 357 367 358 /** 368 359 If the file is not opened, returns $(D false). Otherwise, returns 369 360 $(WEB cplusplus.com/reference/clibrary/cstdio/ferror.html, ferror) for 370 361 the file handle. 371 362 */ 372 bool error() const363 @property bool error() const 373 364 { 374 365 return !p.handle || .ferror(cast(FILE*) p.handle); 375 366 } 376 367 377 368 /** 378 369 Detaches from the underlying file. If the sole owner, calls $(D close) 379 370 and throws if that fails. 380 371 */ 381 372 void detach() 382 373 { … … 514 505 p.name, "'")); 515 506 } 516 507 517 508 unittest 518 509 { 519 510 auto f = File("deleteme", "w"); 520 511 scope(exit) std.file.remove("deleteme"); 521 512 f.rawWrite("\r\n\n\r\n"); 522 513 f.close(); 523 514 assert(std.file.read("deleteme") == "\r\n\n\r\n"); 524 /+525 stdout.rawWrite("\r\n\n\r\n");526 +/527 515 } 528 516 529 517 /** 530 518 If the file is not opened, throws an exception. Otherwise, calls $(WEB 531 519 cplusplus.com/reference/clibrary/cstdio/fseek.html, fseek) for the 532 520 file handle. Throws on error. 533 521 */ 534 522 void seek(long offset, int origin = SEEK_SET) 535 523 { 536 524 enforce(p && p.handle, … … 556 544 return result; 557 545 } 558 546 559 547 /** 560 548 If the file is not opened, throws an exception. Otherwise, calls $(WEB 561 549 cplusplus.com/reference/clibrary/cstdio/_rewind.html, _rewind) for the 562 550 file handle. Throws on error. 563 551 */ 564 552 void rewind() 565 553 { 566 enforce(p && p.handle, 567 "Attempting to rewind() an unopened file"); 554 enforce(isOpen, "Attempting to rewind() an unopened file"); 568 555 .rewind(p.handle); 569 556 } 570 557 571 558 /** 572 559 If the file is not opened, throws an exception. Otherwise, calls $(WEB 573 560 cplusplus.com/reference/clibrary/cstdio/_setvbuf.html, _setvbuf) for 574 561 the file handle. 575 562 */ 576 563 void setvbuf(size_t size, int mode = _IOFBF) 577 564 { 578 errnoEnforce( 579 .setvbuf(enforce(p.handle, 580 "Attempting to call setvbuf() on an unopened file"), 581 null, mode, size) == 0, 582 "Could not set buffering for file `"~p.name~"'"); 565 enforce(isOpen, "Attempting to call setvbuf() on an unopened file"); 566 errnoEnforce(.setvbuf(p.handle, null, mode, size) == 0, 567 "Could not set buffering for file `"~p.name~"'"); 583 568 } 584 569 585 570 /** 586 571 If the file is not opened, throws an exception. Otherwise, calls 587 572 $(WEB cplusplus.com/reference/clibrary/cstdio/_setvbuf.html, 588 573 _setvbuf) for the file handle. */ 589 574 void setvbuf(void[] buf, int mode = _IOFBF) 590 575 { 591 errnoEnforce( 592 .setvbuf(enforce(p.handle, 593 "Attempting to call setvbuf() on an unopened file"), 594 cast(char*) buf.ptr, mode, buf.length) == 0, 595 "Could not set buffering for file `"~p.name~"'"); 576 enforce(isOpen, "Attempting to call setvbuf() on an unopened file"); 577 errnoEnforce(.setvbuf(p.handle, 578 cast(char*) buf.ptr, mode, buf.length) == 0, 579 "Could not set buffering for file `"~p.name~"'"); 596 580 } 597 581 598 582 /** 599 583 If the file is not opened, throws an exception. Otherwise, writes its 600 584 arguments in text format to the file. */ 601 585 void write(S...)(S args) 602 586 { 603 auto w = lockingTextWriter ;587 auto w = lockingTextWriter(); 604 588 foreach (arg; args) 605 589 { 606 590 static if (isSomeString!(typeof(arg))) 607 591 { 608 592 w.put(arg); 609 593 } 610 594 else 611 595 { 612 596 std.format.formattedWrite(w, "%s", arg); 613 597 } 614 598 } 615 599 } 616 600 617 601 /** 618 602 If the file is not opened, throws an exception. Otherwise, writes its 619 603 arguments in text format to the file, followed by a newline. */ 620 604 void writeln(S...)(S args) 621 605 { 622 606 write(args, '\n'); 623 .fflush(p.handle); 607 errnoEnforce(.fflush(p.handle) == 0, 608 "Could not flush file `"~p.name~"'"); 624 609 } 625 610 626 611 private enum errorMessage = 627 612 "You must pass a formatting string as the first" 628 613 " argument to writef or writefln. If no formatting is needed," 629 614 " you may want to use write or writeln."; 630 615 631 616 /** 632 617 If the file is not opened, throws an exception. Otherwise, writes its 633 618 arguments in text format to the file, according to the format in the … … 767 752 char[] buf; 768 753 while (f.readln(buf, "\n\r")) 769 754 { 770 755 assert(i < witness.length); 771 756 assert(buf == witness[i++]); 772 757 } 773 758 } 774 759 775 760 size_t readf(Data...)(in char[] format, Data data) 776 761 { 777 auto input = InputByChar(this); 762 assert(isOpen); 763 auto input = LockingTextReader(this); 778 764 formattedRead(input, format, data); 779 765 return 1; 766 } 767 768 unittest 769 { 770 std.file.write("deleteme", "hello\nworld\n"); 771 scope(exit) std.file.remove("deleteme"); 772 string s; 773 auto f = File("deleteme"); 774 f.readf("%s\n", &s); 775 assert(s == "hello", "["~s~"]"); 780 776 } 781 777 782 778 /** 783 779 Returns a temporary file by calling $(WEB 784 780 cplusplus.com/reference/clibrary/cstdio/_tmpfile.html, _tmpfile). */ 785 781 static File tmpfile() 786 782 { 787 783 auto h = errnoEnforce(core.stdc.stdio.tmpfile, 788 784 "Could not create temporary file with tmpfile()"); 789 785 File result = void; … … 1127 1123 1128 1124 // void opAssign(LockingTextWriter rhs) 1129 1125 // { 1130 1126 // swap(this, rhs); 1131 1127 // } 1132 1128 } 1133 1129 1134 1130 /// Convenience function. 1135 1131 LockingTextWriter lockingTextWriter() 1136 1132 { 1137 // @@@BUG2341@@@1138 1133 return LockingTextWriter(this); 1139 // The code below avoids bug 2341 1140 //printf("Entering fn with %d refs\n", *_refs); 1141 // auto result = LockingTextWriter(this); 1142 // return result; 1143 } 1144 } 1145 1146 struct InputByChar 1147 { 1148 private File _f; 1149 private dchar _crt; 1150 1151 this(File f) 1152 { 1153 _f = f; 1154 } 1155 1156 bool empty() 1157 { 1158 return _f.eof; 1159 } 1160 1161 dchar front() 1162 { 1163 assert(!empty); 1164 if (_crt == _crt.init) popFront; 1165 return _crt; 1166 } 1167 1168 void popFront() 1169 { 1170 enforce(_f.p && _f.p.handle, 1171 "Attempt to read from an unopened file."); 1172 assert(!empty); 1173 _crt = getc(cast(FILE*) _f.p.handle); 1174 } 1175 1176 void unget(dchar c) 1177 { 1178 ungetc(c, cast(FILE*) _f.p.handle); 1179 } 1180 } 1134 } 1135 } 1136 1137 struct LockingTextReader 1138 { 1139 private File _f; 1140 private dchar _crt; 1141 1142 this(File f) 1143 { 1144 enforce(f.isOpen); 1145 _f = f; 1146 FLOCK(_f.p.handle); 1147 } 1148 1149 this(this) 1150 { 1151 FLOCK(_f.p.handle); 1152 } 1153 1154 ~this() 1155 { 1156 // File locking has its own reference count 1157 if (_f.isOpen) FUNLOCK(_f.p.handle); 1158 } 1159 1160 void opAssign(LockingTextReader r) 1161 { 1162 swap(this, r); 1163 } 1164 1165 @property bool empty() 1166 { 1167 if (!_f.isOpen || _f.eof) return true; 1168 if (_crt == _crt.init) 1169 { 1170 _crt = FGETC(cast(_iobuf*) _f.p.handle); 1171 if (_crt == -1) 1172 { 1173 clear(_f); 1174 return true; 1175 } 1176 else 1177 { 1178 enforce(ungetc(_crt, cast(FILE*) _f.p.handle) == _crt); 1179 } 1180 } 1181 return false; 1182 } 1183 1184 dchar front() 1185 { 1186 enforce(!empty); 1187 return _crt; 1188 } 1189 1190 void popFront() 1191 { 1192 enforce(!empty); 1193 if (FGETC(cast(_iobuf*) _f.p.handle) == -1) 1194 { 1195 enforce(_f.eof); 1196 } 1197 _crt = _crt.init; 1198 } 1199 1200 // void unget(dchar c) 1201 // { 1202 // ungetc(c, cast(FILE*) _f.p.handle); 1203 // } 1204 } 1181 1205 1182 1206 unittest 1183 1207 { 1184 // std.file.write("deleteme", "1 2 3"); 1185 // int x, y; 1186 // auto f = File("deleteme"); 1187 // scope(exit) f.close; 1188 // //auto input = InputByChar(f); 1189 // //std.format.formattedRead(input, "%d ", &x); 1190 // f.readf("%d ", &x); 1191 // assert(x == 1); 1192 // f.readf("%d ", &x); 1193 // assert(x == 2); 1194 // //f.readf("%d ", &x); 1195 // assert(x == 3); 1196 pragma(msg, "--- todo: readf ---"); 1208 std.file.write("deleteme", "1 2 3"); 1209 int x, y; 1210 auto f = File("deleteme"); 1211 f.readf("%s ", &x); 1212 assert(x == 1); 1213 f.readf("%d ", &x); 1214 assert(x == 2); 1215 f.readf("%d ", &x); 1216 assert(x == 3); 1217 //pragma(msg, "--- todo: readf ---"); 1197 1218 } 1198 1219 1199 1220 private 1200 1221 void writefx(FILE* fps, TypeInfo[] arguments, void* argptr, int newline=false) 1201 1222 { 1202 1223 int orientation = fwide(fps, 0); // move this inside the lock? 1203 1224 1204 1225 /* Do the file stream locking at the outermost level 1205 1226 * rather than character by character. 1206 1227 */ … … 1480 1501 writeln(b); 1481 1502 } 1482 1503 stdout.close; 1483 1504 auto read = cast(char[]) std.file.read(file); 1484 1505 version (Windows) 1485 1506 assert(read == "Hello, nice world number 42!\r\n1\r\n1\r\n1\r\n", read); 1486 1507 else 1487 1508 assert(read == "Hello, nice world number 42!\n1\n1\n1\n", "["~read~"]"); 1488 1509 } 1489 1510 1511 /** 1512 * Formatted read one line from stdin. 1513 */ 1514 void readf(A...)(in char[] format, A args) 1515 { 1516 return stdin.readf(format, args); 1517 } 1518 1519 unittest 1520 { 1521 float f; 1522 if (false) readf("%s", &f); 1523 } 1524 1490 1525 /********************************** 1491 1526 * Read line from stream $(D fp). 1492 1527 * Returns: 1493 1528 * $(D null) for end of file, 1494 1529 * $(D char[]) for line read from $(D fp), including terminating character 1495 1530 * Params: 1496 1531 * $(D fp) = input stream 1497 1532 * $(D terminator) = line terminator, '\n' by default 1498 1533 * Throws: 1499 1534 * $(D StdioException) on error … … 2018 2053 scope(exit) f.close; 2019 2054 auto t = [ tuple(1, 2), tuple(4, 1), tuple(5, 100) ]; 2020 2055 uint i; 2021 2056 foreach (e; f.byRecord!(int, int)("%s %s")) 2022 2057 { 2023 2058 //writeln(e); 2024 2059 assert(e == t[i++]); 2025 2060 } 2026 2061 assert(i == 3); 2027 2062 } 2028 // {2029 // std.file.write("deleteme", "1:2 3\n4:1 5\n5:100");2030 // File f = File("deleteme");2031 // scope(exit) f.close;2032 // auto t = [ tuple(1, [2,3][]), tuple(4, [1,5][]), tuple(5, [100][]) ];2033 // uint i;2034 // foreach (e; f.byRecord!(int, int[])("%s:%(s )"))2035 // {2036 // //writeln(e);2037 // assert(e == t[i++]);2038 // }2039 // assert(i == 3);2040 // }2041 2063 } 2042 2064 2043 2065 // Private implementation of readln 2066 version (DIGITAL_MARS_STDIO) 2044 2067 private size_t readlnImpl(FILE* fps, ref char[] buf, dchar terminator = '\n') 2045 2068 { 2046 version (DIGITAL_MARS_STDIO) 2047 { 2048 FLOCK(fps); 2049 scope(exit) FUNLOCK(fps); 2069 FLOCK(fps); 2070 scope(exit) FUNLOCK(fps); 2050 2071 2051 2072 /* Since fps is now locked, we can create an "unshared" version 2052 2073 * of fp. 2053 2074 */ 2054 2075 auto fp = cast(_iobuf*)fps; 2055 2076 2056 if (__fhnd_info[fp._file] & FHND_WCHAR) 2057 { /* Stream is in wide characters. 2058 * Read them and convert to chars. 2077 if (__fhnd_info[fp._file] & FHND_WCHAR) 2078 { /* Stream is in wide characters. 2079 * Read them and convert to chars. 2080 */ 2081 static assert(wchar_t.sizeof == 2); 2082 auto app = appender(&buf); 2083 buf.length = 0; 2084 for (int c = void; (c = FGETWC(fp)) != -1; ) 2085 { 2086 if ((c & ~0x7F) == 0) 2087 { app.put(cast(char) c); 2088 if (c == terminator) 2089 break; 2090 } 2091 else 2092 { 2093 if (c >= 0xD800 && c <= 0xDBFF) 2094 { 2095 int c2 = void; 2096 if ((c2 = FGETWC(fp)) != -1 || 2097 c2 < 0xDC00 && c2 > 0xDFFF) 2098 { 2099 StdioException("unpaired UTF-16 surrogate"); 2100 } 2101 c = ((c - 0xD7C0) << 10) + (c2 - 0xDC00); 2102 } 2103 std.utf.encode(buf, c); 2104 } 2105 } 2106 if (ferror(fps)) 2107 StdioException(); 2108 return buf.length; 2109 } 2110 2111 auto sz = GC.sizeOf(buf.ptr); 2112 //auto sz = buf.length; 2113 buf = buf.ptr[0 .. sz]; 2114 if (fp._flag & _IONBF) 2115 { 2116 /* Use this for unbuffered I/O, when running 2117 * across buffer boundaries, or for any but the common 2118 * cases. 2119 */ 2120 L1: 2121 if(buf.ptr is null) 2122 { 2123 sz = 128; 2124 auto p = cast(char*) GC.malloc(sz, GC.BlkAttr.NO_SCAN); 2125 buf = p[0 .. 0]; 2126 } else { 2127 buf.length = 0; 2128 } 2129 2130 auto app = appender(&buf); 2131 int c; 2132 while((c = FGETC(fp)) != -1) { 2133 app.put(cast(char) c); 2134 if(buf[$ - 1] == terminator) { 2135 return buf.length; 2136 } 2137 2138 } 2139 2140 if (ferror(fps)) 2141 StdioException(); 2142 return buf.length; 2143 } 2144 else 2145 { 2146 int u = fp._cnt; 2147 char* p = fp._ptr; 2148 int i; 2149 if (fp._flag & _IOTRAN) 2150 { /* Translated mode ignores \r and treats ^Z as end-of-file 2059 2151 */ 2060 static assert(wchar_t.sizeof == 2); 2061 auto app = appender(&buf); 2152 char c; 2153 while (1) 2154 { 2155 if (i == u) // if end of buffer 2156 goto L1; // give up 2157 c = p[i]; 2158 i++; 2159 if (c != '\r') 2160 { 2161 if (c == terminator) 2162 break; 2163 if (c != 0x1A) 2164 continue; 2165 goto L1; 2166 } 2167 else 2168 { if (i != u && p[i] == terminator) 2169 break; 2170 goto L1; 2171 } 2172 } 2173 if (i > sz) 2174 { 2175 buf = cast(char[])GC.malloc(i, GC.BlkAttr.NO_SCAN)[0 .. i]; 2176 } 2177 if (i - 1) 2178 memcpy(buf.ptr, p, i - 1); 2179 buf[i - 1] = cast(char)terminator; 2180 buf = buf[0 .. i]; 2181 if (terminator == '\n' && c == '\r') 2182 i++; 2183 } 2184 else 2185 { 2186 while (1) 2187 { 2188 if (i == u) // if end of buffer 2189 goto L1; // give up 2190 auto c = p[i]; 2191 i++; 2192 if (c == terminator) 2193 break; 2194 } 2195 if (i > sz) 2196 { 2197 buf = cast(char[])GC.malloc(i, GC.BlkAttr.NO_SCAN)[0 .. i]; 2198 } 2199 memcpy(buf.ptr, p, i); 2200 buf = buf[0 .. i]; 2201 } 2202 fp._cnt -= i; 2203 fp._ptr += i; 2204 return i; 2205 } 2206 } 2207 2208 version (GCC_IO) 2209 private size_t readlnImpl(FILE* fps, ref char[] buf, dchar terminator = '\n') 2210 { 2211 if (fwide(fps, 0) > 0) 2212 { /* Stream is in wide characters. 2213 * Read them and convert to chars. 2214 */ 2215 FLOCK(fps); 2216 scope(exit) FUNLOCK(fps); 2217 auto fp = cast(_iobuf*)fps; 2218 version (Windows) 2219 { 2062 2220 buf.length = 0; 2063 int c2;2064 2221 for (int c = void; (c = FGETWC(fp)) != -1; ) 2065 2222 { 2066 2223 if ((c & ~0x7F) == 0) 2067 { app.put(cast(char) c);2224 { buf ~= c; 2068 2225 if (c == terminator) 2069 2226 break; 2070 2227 } 2071 2228 else 2072 2229 { 2073 2230 if (c >= 0xD800 && c <= 0xDBFF) 2074 2231 { 2232 int c2 = void; 2075 2233 if ((c2 = FGETWC(fp)) != -1 || 2076 2234 c2 < 0xDC00 && c2 > 0xDFFF) 2077 2235 { 2078 2236 StdioException("unpaired UTF-16 surrogate"); 2079 2237 } 2080 2238 c = ((c - 0xD7C0) << 10) + (c2 - 0xDC00); 2081 2239 } 2082 2240 std.utf.encode(buf, c); 2083 2241 } 2084 2242 } 2243 if (ferror(fp)) 2244 StdioException(); 2245 return buf.length; 2246 } 2247 else version (Posix) 2248 { 2249 buf.length = 0; 2250 for (int c; (c = FGETWC(fp)) != -1; ) 2251 { 2252 if ((c & ~0x7F) == 0) 2253 buf ~= cast(char)c; 2254 else 2255 std.utf.encode(buf, cast(dchar)c); 2256 if (c == terminator) 2257 break; 2258 } 2085 2259 if (ferror(fps)) 2086 2260 StdioException(); 2087 2261 return buf.length; 2088 2262 } 2089 2090 auto sz = GC.sizeOf(buf.ptr); 2091 //auto sz = buf.length; 2092 buf = buf.ptr[0 .. sz]; 2093 if (fp._flag & _IONBF) 2094 { 2095 /* Use this for unbuffered I/O, when running 2096 * across buffer boundaries, or for any but the common 2097 * cases. 2098 */ 2099 L1: 2100 if(buf.ptr is null) 2101 { 2102 sz = 128; 2103 auto p = cast(char*) GC.malloc(sz, GC.BlkAttr.NO_SCAN); 2104 buf = p[0 .. 0]; 2105 } else { 2106 buf.length = 0; 2107 } 2108 2109 auto app = appender(&buf); 2110 int c; 2111 while((c = FGETC(fp)) != -1) { 2112 app.put(cast(char) c); 2113 if(buf[$ - 1] == terminator) { 2114 return buf.length; 2263 else 2264 { 2265 static assert(0); 2266 } 2267 } 2268 2269 char *lineptr = null; 2270 size_t n = 0; 2271 auto s = getdelim(&lineptr, &n, terminator, fps); 2272 scope(exit) free(lineptr); 2273 if (s < 0) 2274 { 2275 if (ferror(fps)) 2276 StdioException(); 2277 buf.length = 0; // end of file 2278 return 0; 2279 } 2280 buf = buf.ptr[0 .. GC.sizeOf(buf.ptr)]; 2281 if (s <= buf.length) 2282 { 2283 buf.length = s; 2284 buf[] = lineptr[0 .. s]; 2285 } 2286 else 2287 { 2288 buf = lineptr[0 .. s].dup; 2289 } 2290 return s; 2291 } 2292 2293 version (GENERIC_IO) 2294 private size_t readlnImpl(FILE* fps, ref char[] buf, dchar terminator = '\n') 2295 { 2296 FLOCK(fps); 2297 scope(exit) FUNLOCK(fps); 2298 auto fp = cast(_iobuf*)fps; 2299 if (fwide(fps, 0) > 0) 2300 { /* Stream is in wide characters. 2301 * Read them and convert to chars. 2302 */ 2303 version (Windows) 2304 { 2305 buf.length = 0; 2306 for (int c; (c = FGETWC(fp)) != -1; ) 2307 { 2308 if ((c & ~0x7F) == 0) 2309 { buf ~= c; 2310 if (c == terminator) 2311 break; 2115 2312 } 2116 2117 } 2118 2313 else 2314 { 2315 if (c >= 0xD800 && c <= 0xDBFF) 2316 { 2317 int c2 = void; 2318 if ((c2 = FGETWC(fp)) != -1 || 2319 c2 < 0xDC00 && c2 > 0xDFFF) 2320 { 2321 StdioException("unpaired UTF-16 surrogate"); 2322 } 2323 c = ((c - 0xD7C0) << 10) + (c2 - 0xDC00); 2324 } 2325 std.utf.encode(buf, c); 2326 } 2327 } 2328 if (ferror(fp)) 2329 StdioException(); 2330 return buf.length; 2331 } 2332 else version (Posix) 2333 { 2334 buf.length = 0; 2335 for (int c; (c = FGETWC(fp)) != -1; ) 2336 { 2337 if ((c & ~0x7F) == 0) 2338 buf ~= cast(char)c; 2339 else 2340 std.utf.encode(buf, cast(dchar)c); 2341 if (c == terminator) 2342 break; 2343 } 2119 2344 if (ferror(fps)) 2120 2345 StdioException(); 2121 2346 return buf.length; 2122 2347 } 2123 2348 else 2124 2349 { 2125 int u = fp._cnt; 2126 char* p = fp._ptr; 2127 int i; 2128 if (fp._flag & _IOTRAN) 2129 { /* Translated mode ignores \r and treats ^Z as end-of-file 2130 */ 2131 char c; 2132 while (1) 2133 { 2134 if (i == u) // if end of buffer 2135 goto L1; // give up 2136 c = p[i]; 2137 i++; 2138 if (c != '\r') 2139 { 2140 if (c == terminator) 2141 break; 2142 if (c != 0x1A) 2143 continue; 2144 goto L1; 2145 } 2146 else 2147 { if (i != u && p[i] == terminator) 2148 break; 2149 goto L1; 2150 } 2151 } 2152 if (i > sz) 2153 { 2154 buf = cast(char[])GC.malloc(i, GC.BlkAttr.NO_SCAN)[0 .. i]; 2155 } 2156 if (i - 1) 2157 memcpy(buf.ptr, p, i - 1); 2158 buf[i - 1] = cast(char)terminator; 2159 buf = buf[0 .. i]; 2160 if (terminator == '\n' && c == '\r') 2161 i++; 2162 } 2163 else 2164 { 2165 while (1) 2166 { 2167 if (i == u) // if end of buffer 2168 goto L1; // give up 2169 auto c = p[i]; 2170 i++; 2171 if (c == terminator) 2172 break; 2173 } 2174 if (i > sz) 2175 { 2176 buf = cast(char[])GC.malloc(i, GC.BlkAttr.NO_SCAN)[0 .. i]; 2177 } 2178 memcpy(buf.ptr, p, i); 2179 buf = buf[0 .. i]; 2180 } 2181 fp._cnt -= i; 2182 fp._ptr += i; 2183 return i; 2184 } 2185 } 2186 else version (GCC_IO) 2187 { 2188 if (fwide(fps, 0) > 0) 2189 { /* Stream is in wide characters. 2190 * Read them and convert to chars. 2191 */ 2192 FLOCK(fps); 2193 scope(exit) FUNLOCK(fps); 2194 auto fp = cast(_iobuf*)fps; 2195 version (Windows) 2196 { 2197 buf.length = 0; 2198 int c2; 2199 for (int c = void; (c = FGETWC(fp)) != -1; ) 2200 { 2201 if ((c & ~0x7F) == 0) 2202 { buf ~= c; 2203 if (c == terminator) 2204 break; 2205 } 2206 else 2207 { 2208 if (c >= 0xD800 && c <= 0xDBFF) 2209 { 2210 if ((c2 = FGETWC(fp)) != -1 || 2211 c2 < 0xDC00 && c2 > 0xDFFF) 2212 { 2213 StdioException("unpaired UTF-16 surrogate"); 2214 } 2215 c = ((c - 0xD7C0) << 10) + (c2 - 0xDC00); 2216 } 2217 std.utf.encode(buf, c); 2218 } 2219 } 2220 if (ferror(fp)) 2221 StdioException(); 2222 return buf.length; 2223 } 2224 else version (Posix) 2225 { 2226 buf.length = 0; 2227 for (int c; (c = FGETWC(fp)) != -1; ) 2228 { 2229 if ((c & ~0x7F) == 0) 2230 buf ~= cast(char)c; 2231 else 2232 std.utf.encode(buf, cast(dchar)c); 2233 if (c == terminator) 2234 break; 2235 } 2236 if (ferror(fps)) 2237 StdioException(); 2238 return buf.length; 2239 } 2240 else 2241 { 2242 static assert(0); 2243 } 2244 } 2245 2246 char *lineptr = null; 2247 size_t n = 0; 2248 auto s = getdelim(&lineptr, &n, terminator, fps); 2249 scope(exit) free(lineptr); 2250 if (s < 0) 2251 { 2252 if (ferror(fps)) 2253 StdioException(); 2254 buf.length = 0; // end of file 2255 return 0; 2256 } 2257 buf = buf.ptr[0 .. GC.sizeOf(buf.ptr)]; 2258 if (s <= buf.length) 2259 { 2260 buf.length = s; 2261 buf[] = lineptr[0 .. s]; 2262 } 2263 else 2264 { 2265 buf = lineptr[0 .. s].dup; 2266 } 2267 return s; 2268 } 2269 else version (GENERIC_IO) 2270 { 2271 FLOCK(fps); 2272 scope(exit) FUNLOCK(fps); 2273 auto fp = cast(_iobuf*)fps; 2274 if (fwide(fps, 0) > 0) 2275 { /* Stream is in wide characters. 2276 * Read them and convert to chars. 2277 */ 2278 version (Windows) 2279 { 2280 buf.length = 0; 2281 int c2; 2282 for (int c; (c = FGETWC(fp)) != -1; ) 2283 { 2284 if ((c & ~0x7F) == 0) 2285 { buf ~= c; 2286 if (c == terminator) 2287 break; 2288 } 2289 else 2290 { 2291 if (c >= 0xD800 && c <= 0xDBFF) 2292 { 2293 if ((c2 = FGETWC(fp)) != -1 || 2294 c2 < 0xDC00 && c2 > 0xDFFF) 2295 { 2296 StdioException("unpaired UTF-16 surrogate"); 2297 } 2298 c = ((c - 0xD7C0) << 10) + (c2 - 0xDC00); 2299 } 2300 std.utf.encode(buf, c); 2301 } 2302 } 2303 if (ferror(fp)) 2304 StdioException(); 2305 return buf.length; 2306 } 2307 else version (Posix) 2308 { 2309 buf.length = 0; 2310 for (int c; (c = FGETWC(fp)) != -1; ) 2311 { 2312 if ((c & ~0x7F) == 0) 2313 buf ~= cast(char)c; 2314 else 2315 std.utf.encode(buf, cast(dchar)c); 2316 if (c == terminator) 2317 break; 2318 } 2319 if (ferror(fps)) 2320 StdioException(); 2321 return buf.length; 2322 } 2323 else 2324 { 2325 static assert(0); 2326 } 2327 } 2328 2329 buf.length = 0; 2330 for (int c; (c = FGETC(fp)) != -1; ) 2331 { 2332 buf ~= cast(char)c; 2333 if (c == terminator) 2334 break; 2335 } 2336 if (ferror(fps)) 2337 StdioException(); 2338 return buf.length; 2339 } 2340 else 2341 { 2342 static assert(0); 2343 } 2344 } 2350 static assert(0); 2351 } 2352 } 2353 2354 // Narrow stream 2355 buf.length = 0; 2356 for (int c; (c = FGETC(fp)) != -1; ) 2357 { 2358 buf ~= cast(char)c; 2359 if (c == terminator) 2360 break; 2361 } 2362 if (ferror(fps)) 2363 StdioException(); 2364 return buf.length; 2365 }
