my class IO::Handle { has $.path; has $!PIO; has $.chomp is rw = Bool::True; has $.nl-in = ["\x0A", "\r\n"]; has Str:D $.nl-out is rw = "\n"; has Str $.encoding; has Encoding::Decoder $!decoder; has Encoding::Encoder $!encoder; has int $!out-buffer; submethod TWEAK (:$encoding, :$bin, IO() :$!path = Nil) { $bin ?? nqp::isconcrete($encoding) && X::IO::BinaryAndEncoding.new.throw !! ($!encoding = $encoding || 'utf8') } #?if moar # Make sure we close any open files on exit my $opened := nqp::list; my $opened-locker := Lock.new; method !remember-to-close(--> Nil) { $opened-locker.protect: { nqp::setelems($opened,nqp::elems($opened) + 1024) if nqp::isge_i(nqp::filenofh($!PIO),nqp::elems($opened)); nqp::bindpos($opened,nqp::filenofh($!PIO),$!PIO); } } method !forget-about-closing(--> Nil) { $opened-locker.protect: { nqp::bindpos($opened,nqp::filenofh($!PIO),nqp::null) } } method !close-all-open-handles(--> Nil) { if nqp::elems($opened) -> int $elems { my int $i = 2; # skip STDIN, STDOUT, STDERR nqp::while( nqp::islt_i(++$i,$elems), nqp::unless( nqp::isnull(my $PIO := nqp::atpos($opened,$i)), nqp::closefh($PIO) ) ) } } method do-not-close-automatically(IO::Handle:D: --> Bool:D) { if nqp::defined($!PIO) { self!forget-about-closing; True } else { False } } #?endif #?if !moar method do-not-close-automatically(IO::Handle:D: --> False) { } #?endif method open(IO::Handle:D: :$r, :$w, :$x, :$a, :$update, :$rw, :$rx, :$ra, :$mode is copy, :$create is copy, :$append is copy, :$truncate is copy, :$exclusive is copy, :$bin, :$enc is copy, :$chomp = $!chomp, :$nl-in is copy = $!nl-in, Str:D :$nl-out is copy = $!nl-out, :$out-buffer is copy, ) { nqp::if( $bin, nqp::stmts( nqp::isconcrete($enc) && X::IO::BinaryAndEncoding.new.throw, $!encoding = Nil), nqp::unless( nqp::isconcrete($enc), $enc = $!encoding)); $mode = nqp::if( $mode, nqp::if(nqp::istype($mode, Str), $mode, $mode.Str), nqp::if($w && $r || $rw, nqp::stmts(($create = True), 'rw'), nqp::if($x && $r || $rx, nqp::stmts(($create = $exclusive = True), 'rw'), nqp::if($a && $r || $ra, nqp::stmts(($create = $append = True), 'rw'), nqp::if($r, 'ro', nqp::if($w, nqp::stmts(($create = $truncate = True), 'wo'), nqp::if($x, nqp::stmts(($create = $exclusive = True), 'wo'), nqp::if($a, nqp::stmts(($create = $append = True), 'wo'), nqp::if($update, 'rw', 'ro'))))))))); nqp::if( nqp::iseq_s($!path.Str, '-'), nqp::if( nqp::iseq_s($mode, 'ro'), nqp::if( $*IN.opened, nqp::stmts( $*IN.encoding($enc), return $*IN), nqp::stmts( nqp::if( nqp::iseq_s($*IN.path.Str, '-'), $*IN = IO::Handle.new: :path(IO::Special.new: '')), return $*IN.open: :$enc, :bin(nqp::isfalse(nqp::isconcrete($enc))))), nqp::if( nqp::iseq_s($mode, 'wo'), nqp::if( $*OUT.opened, nqp::stmts( $*OUT.encoding($enc), return $*OUT), nqp::stmts( nqp::if( nqp::iseq_s($*OUT.path.Str, '-'), $*OUT = IO::Handle.new: :path(IO::Special.new: '')), return $*OUT.open: :w, :$enc, :bin(nqp::isfalse(nqp::isconcrete($enc))))), die("Cannot open standard stream in mode '$mode'")))); if nqp::istype($!path, IO::Special) { my $what := $!path.what; if $what eq '' { $!PIO := nqp::getstdin(); } elsif $what eq '' { $!PIO := nqp::getstdout(); } elsif $what eq '' { $!PIO := nqp::getstderr(); } else { die "Don't know how to open '$_' especially"; } $!chomp = $chomp; $!nl-out = $nl-out; if nqp::isconcrete($enc) { my $encoding = Encoding::Registry.find($enc); $!decoder := $encoding.decoder(:translate-nl); $!decoder.set-line-separators(($!nl-in = $nl-in).list); $!encoder := $encoding.encoder(:translate-nl); $!encoding = $encoding.name; } self!set-out-buffer-size($out-buffer); return self; } fail X::IO::Directory.new(:$!path, :trying) if $!path.d; { CATCH { .fail } $!PIO := nqp::open( $!path.absolute, nqp::concat( nqp::if(nqp::iseq_s($mode, 'ro'), 'r', nqp::if(nqp::iseq_s($mode, 'wo'), '-', nqp::if(nqp::iseq_s($mode, 'rw'), '+', die "Unknown mode '$mode'"))), nqp::concat(nqp::if($create, 'c', ''), nqp::concat(nqp::if($append, 'a', ''), nqp::concat(nqp::if($truncate, 't', ''), nqp::if($exclusive, 'x', '')))))); #?if moar self!remember-to-close; #?endif } $!chomp = $chomp; $!nl-out = $nl-out; if nqp::isconcrete($enc) { my $encoding = Encoding::Registry.find($enc); $!decoder := $encoding.decoder(:translate-nl); $!decoder.set-line-separators(($!nl-in = $nl-in).list); $!encoder := $encoding.encoder(:translate-nl); $!encoding = $encoding.name; # Add a byte order mark to the start of the file for utf16 nqp::if(nqp::iseq_s($!encoding, 'utf16'), ( if $create && !$exclusive && (!$append || $append && $!path.s == 0) { self.write: Buf[uint16].new(0xFEFF); }) ); } self!set-out-buffer-size($out-buffer); self; } method out-buffer is rw { Proxy.new: :FETCH{ $!out-buffer }, STORE => -> $, \buffer { self!set-out-buffer-size: buffer; } } method !set-out-buffer-size($buffer is copy) { $buffer //= !nqp::isttyfh($!PIO); $!out-buffer = nqp::istype($buffer, Bool) ?? ($buffer ?? 8192 !! 0) !! $buffer.Int; nqp::setbuffersizefh($!PIO, $!out-buffer); $!out-buffer } method nl-in is rw { Proxy.new( FETCH => { $!nl-in }, STORE => -> $, $nl-in { $!nl-in = $nl-in; $!decoder && $!decoder.set-line-separators($nl-in.list); $nl-in } ); } method close(IO::Handle:D: --> True) { nqp::if( nqp::defined($!PIO), nqp::stmts( nqp::if( nqp::isconcrete($!decoder), ($!decoder := Encoding::Decoder) ), #?if moar self!forget-about-closing, # mark as closed #?endif nqp::closefh($!PIO), # close, ignore errors $!PIO := nqp::null # mark HLL handle now also closed ) ) } method eof(IO::Handle:D:) { nqp::hllbool($!decoder ?? $!decoder.is-empty && self.EOF !! self.EOF) } method EOF() { nqp::not_i(nqp::defined($!PIO)) || nqp::eoffh($!PIO) } method READ(Int:D $bytes) { nqp::readfh($!PIO,nqp::create(buf8.^pun),$bytes) } method !failed($trying) { ($!PIO ?? X::IO::BinaryMode !! X::IO::Closed).new(:$trying).throw } method get(IO::Handle:D:) { $!decoder ?? $!decoder.consume-line-chars(:$!chomp) // self!get-line-slow-path() !! self!failed('get') } method !get-line-slow-path() { my $line := Nil; unless self.EOF && $!decoder.is-empty { loop { my $buf := self.READ(0x100000); if $buf.elems { $!decoder.add-bytes($buf); $line := $!decoder.consume-line-chars(:$!chomp); last if nqp::isconcrete($line); } else { $line := $!decoder.consume-line-chars(:$!chomp, :eof) unless self.EOF && $!decoder.is-empty; last; } } } $line } method getc(IO::Handle:D:) { $!decoder ?? $!decoder.consume-exactly-chars(1) || (self!readchars-slow-path(1) || Nil) !! self!failed('getc') } # XXX TODO: Make these routine read handle lazily when we have Cat type method comb (IO::Handle:D: :$close, |c) { $!decoder ?? self.slurp(:$close).comb( |c ) !! self!failed('comb') } method split(IO::Handle:D: :$close, |c) { $!decoder ?? self.slurp(:$close).split( |c ) !! self!failed('split') } proto method words (|) {*} multi method words(IO::Handle:D \SELF: $limit, :$close) { $!decoder ?? nqp::istype($limit,Whatever) || $limit == Inf ?? self.words(:$close) !! $close ?? Seq.new(Rakudo::Iterator.FirstNThenSinkAll( self.words.iterator, $limit.Int, {SELF.close})) !! self.words.head($limit.Int) !! self!failed('words') } my class Words does Iterator { has $!handle is built(:bind); has $!close is built(:bind); has str $!str= ""; # https://github.com/Raku/old-issue-tracker/issues/4690; has int $!searching = 1; has int $!pos; method TWEAK() { self!next-chunk } method !next-chunk() { my int $chars = nqp::chars($!str); $!str = $!pos < $chars ?? nqp::substr($!str,$!pos) !! ""; $chars = nqp::chars($!str); while $!searching { $!str = nqp::concat($!str,$!handle.readchars); my int $new = nqp::chars($!str); $!searching = 0 if $new == $chars; # end $!pos = ($chars = $new) ?? nqp::findnotcclass( nqp::const::CCLASS_WHITESPACE, $!str, 0, $chars) !! 0; last if $!pos < $chars; } } method pull-one() { my int $chars; my int $left; my int $nextpos; while ($chars = nqp::chars($!str)) && $!searching { while ($left = $chars - $!pos) > 0 { $nextpos = nqp::findcclass( nqp::const::CCLASS_WHITESPACE,$!str,$!pos,$left); last unless $left = $chars - $nextpos; # broken word my str $found = nqp::substr($!str, $!pos, $nextpos - $!pos); $!pos = nqp::findnotcclass( nqp::const::CCLASS_WHITESPACE,$!str,$nextpos,$left); return nqp::p6box_s($found); } self!next-chunk; } if $!pos < $chars { my str $found = nqp::substr($!str,$!pos); $!pos = $chars; nqp::p6box_s($found) } else { $!handle.close if $!close; IterationEnd } } method push-all(\target --> IterationEnd) { my int $chars; my int $left; my int $nextpos; while ($chars = nqp::chars($!str)) && $!searching { while ($left = $chars - $!pos) > 0 { $nextpos = nqp::findcclass( nqp::const::CCLASS_WHITESPACE,$!str,$!pos,$left); last unless $left = $chars - $nextpos; # broken word target.push(nqp::p6box_s( nqp::substr($!str, $!pos, $nextpos - $!pos) )); $!pos = nqp::findnotcclass( nqp::const::CCLASS_WHITESPACE,$!str,$nextpos,$left); } self!next-chunk; } target.push(nqp::p6box_s(nqp::substr($!str,$!pos))) if $!pos < $chars; $!handle.close if $!close; } } multi method words(IO::Handle:D: :$close) { $!decoder ?? Seq.new(Words.new(:handle(self), :$close)) !! self!failed('words') } my class GetLineFast does Iterator { has $!handle; has $!chomp; has $!decoder; has $!close; method new(\handle,\close) { my \res = nqp::create(self); nqp::bindattr(res, self.WHAT, '$!handle', handle); nqp::bindattr(res, self.WHAT, '$!close', close); nqp::bindattr(res, self.WHAT, '$!chomp', nqp::getattr(handle, IO::Handle, '$!chomp')); nqp::p6bindattrinvres(res, self.WHAT, '$!decoder', nqp::getattr(handle, IO::Handle, '$!decoder')) } method pull-one() { # Slow path falls back to .get on the handle, which will # replenish the buffer once we exhaust it. nqp::if( nqp::isconcrete( my \consumed := $!decoder.consume-line-chars(:$!chomp) ), consumed, nqp::if( nqp::isconcrete(my \got := $!handle.get), got, nqp::stmts( nqp::if($!close,$!handle.close), IterationEnd ) ) ) } method push-all(\target --> IterationEnd) { nqp::while( nqp::if( nqp::isconcrete( my \consumed := $!decoder.consume-line-chars(:$!chomp) ), nqp::stmts( target.push(consumed), 1 ), nqp::if( nqp::isconcrete(my \got := $!handle.get), nqp::stmts( target.push(got), 1 ) ) ), nqp::null ); $!handle.close if $!close; } method sink-all(--> IterationEnd) { $!close ?? $!handle.close # can't seek pipes, so need the `try` !! try $!handle.seek(0,SeekFromEnd) # seek to end } } my class GetLineSlow does Iterator { has $!handle; has $!close; method new(\handle,\close) { my \res = nqp::create(self); nqp::bindattr(res, self.WHAT, '$!close', close); nqp::p6bindattrinvres(res,self.WHAT,'$!handle',handle) } method pull-one() { nqp::if( nqp::isconcrete(my \line := $!handle.get), line, nqp::stmts( nqp::if($!close,$!handle.close), IterationEnd ) ) } method push-all(\target --> IterationEnd) { nqp::while( nqp::isconcrete(my \line := $!handle.get), target.push(line) ); $!handle.close if $!close; } method sink-all(--> IterationEnd) { $!close ?? $!handle.close # can't seek pipes, so need the `try` !! try $!handle.seek(0,SeekFromEnd) # seek to end } } method !LINES-ITERATOR(IO::Handle:D: $close) { $!decoder ?? nqp::eqaddr(self.WHAT,IO::Handle) ?? GetLineFast.new(self,$close) # exact type, can shortcircuit !! GetLineSlow.new(self,$close) # can *NOT* shortcircuit .get !! self!failed('lines') } proto method lines (|) {*} multi method lines(IO::Handle:D \SELF: $limit, :$close) { nqp::istype($limit,Whatever) || $limit == Inf ?? Seq.new(self!LINES-ITERATOR($close)) !! $close ?? Seq.new(Rakudo::Iterator.FirstNThenSinkAll( self!LINES-ITERATOR(0), $limit.Int, {SELF.close})) !! self.lines.head($limit.Int) } multi method lines(IO::Handle:D \SELF: :$close) { Seq.new(self!LINES-ITERATOR($close)) } method read(IO::Handle:D: Int(Cool:D) $bytes = $*DEFAULT-READ-ELEMS) { # If we have one, read bytes via. the decoder to support mixed-mode I/O. $!decoder ?? ($!decoder.consume-exactly-bytes($bytes) // self!read-slow-path($bytes)) !! nqp::eqaddr(nqp::what(self),IO::Handle) ?? nqp::readfh($!PIO,nqp::create(buf8.^pun),$bytes) !! self.READ($bytes) } method !read-slow-path($bytes) { if self.EOF && $!decoder.is-empty { nqp::create(buf8.^pun) } else { $!decoder.add-bytes(self.READ($bytes max 0x100000)); $!decoder.consume-exactly-bytes($bytes) // $!decoder.consume-exactly-bytes($!decoder.bytes-available) // nqp::create(buf8.^pun) } } method readchars(Int(Cool:D) $chars = $*DEFAULT-READ-ELEMS) { $!decoder ?? $!decoder.consume-exactly-chars($chars) // self!readchars-slow-path($chars) !! self!failed('readchars') } method !readchars-slow-path($chars) { my $result := ''; unless self.EOF && $!decoder.is-empty { loop { my $buf := self.READ(0x100000); if $buf.elems { $!decoder.add-bytes($buf); $result := $!decoder.consume-exactly-chars($chars); last if nqp::isconcrete($result); } else { $result := $!decoder.consume-exactly-chars($chars, :eof) unless self.EOF && $!decoder.is-empty; last; } } } $result } multi method Supply(IO::Handle:D: :$size = $*DEFAULT-READ-ELEMS --> Supply:D) { if $!decoder { # handle is in character mode supply { my int $chars = $size; my str $str = self.readchars($chars); nqp::while( nqp::chars($str), nqp::stmts( (emit nqp::p6box_s($str)), ($str = self.readchars($chars)) ) ); done; } } else { supply { my $buf := self.read($size); nqp::while( nqp::elems($buf), nqp::stmts( (emit $buf), ($buf := self.read($size)) ) ); done; } } } proto method seek(|) {*} multi method seek(IO::Handle:D: Int:D $offset, SeekType:D $whence = SeekFromBeginning --> True) { my int $rewind = 0; if $!decoder { # consider bytes we pre-read, when seeking from current position: $rewind = $!decoder.bytes-available if nqp::eqaddr(nqp::decont($whence), SeekFromCurrent); # Freshen decoder, so we won't have stuff left over from earlier reads # that were in the wrong place. $!decoder := Encoding::Registry.find($!encoding).decoder(:translate-nl); $!decoder.set-line-separators($!nl-in.list); } nqp::seekfh($!PIO, $offset - $rewind, +$whence); } method tell(IO::Handle:D: --> Int:D) { nqp::tellfh($!PIO) - ($!decoder ?? $!decoder.bytes-available !! 0) } method write(IO::Handle:D: Blob:D $buf --> True) { self.WRITE($buf) } method WRITE(IO::Handle:D: Blob:D $buf --> True) { nqp::writefh($!PIO, nqp::decont($buf)); } method opened(IO::Handle:D:) { nqp::hllbool(nqp::istrue($!PIO)); } method t(IO::Handle:D:) { self.opened && nqp::hllbool(nqp::isttyfh($!PIO)) } method lock(IO::Handle:D: Bool:D :$non-blocking = False, Bool:D :$shared = False --> True ) { CATCH { default { #?if moar self!remember-to-close; #?endif fail X::IO::Lock.new: :os-error(.Str), :lock-type( 'non-' x $non-blocking ~ 'blocking, ' ~ ($shared ?? 'shared' !! 'exclusive') ); }} #?if moar self!forget-about-closing; #?endif nqp::lockfh($!PIO, 0x10*$non-blocking + $shared); } method unlock(IO::Handle:D: --> True) { #?if moar self!remember-to-close; #?endif nqp::unlockfh($!PIO); } method printf(IO::Handle:D: |c) { self.print(sprintf |c); } multi method print(IO::Handle:D: Junction:D \j) { j.THREAD: { self.print: $_ } } multi method print(IO::Handle:D: Str:D \x --> True) { $!decoder ?? self.WRITE($!encoder.encode-chars(x)) !! self!failed('print') } multi method print(IO::Handle:D: \x --> True) { $!decoder ?? self.WRITE($!encoder.encode-chars(x.Str)) !! self!failed('print') } multi method print(IO::Handle:D: | --> True) { self!failed('print') unless $!decoder; my Mu $args := nqp::p6argvmarray; nqp::shift($args); self.WRITE($!encoder.encode-chars( nqp::join("",Rakudo::Internals.StrList2list_s($args)))) } multi method put(IO::Handle:D: Junction:D \j) { j.THREAD: { self.print: nqp::concat(.Str,$!nl-out) } } multi method put(IO::Handle:D: --> True) { $!decoder ?? self.WRITE($!encoder.encode-chars($!nl-out)) !! self!failed('say') } multi method put(IO::Handle:D: \x --> True) { $!decoder ?? self.WRITE($!encoder.encode-chars(nqp::concat(x.Str, $!nl-out))) !! self!failed('put') } multi method put(IO::Handle:D: | --> True) { self!failed('put') unless $!decoder; my Mu $args := nqp::p6argvmarray; nqp::shift($args); my $parts := Rakudo::Internals.StrList2list_s($args); nqp::push_s($parts,$!nl-out); self.WRITE($!encoder.encode-chars(nqp::join("",$parts))) } multi method say(IO::Handle:D: --> True) { $!decoder ?? self.WRITE($!encoder.encode-chars($!nl-out)) !! self!failed('say') } multi method say(IO::Handle:D: \x --> True) { $!decoder ?? self.WRITE($!encoder.encode-chars(nqp::concat(x.gist,$!nl-out))) !! self!failed('say') } multi method say(IO::Handle:D: | --> True) { self!failed('say') unless $!decoder; my Mu $args := nqp::p6argvmarray; nqp::shift($args); my $parts := Rakudo::Internals.GistList2list_s($args); nqp::push_s($parts,$!nl-out); self.WRITE($!encoder.encode-chars(nqp::join("",$parts))) } # there is no special handling, since it is supposed to give a # human readable version of the Junction. Leaving the method here # so that future optimizers will not try to add it. # multi method say(Junction:D \j) { j.THREAD: { self.say: $_ } } method print-nl(IO::Handle:D: --> True) { $!decoder ?? self.WRITE($!encoder.encode-chars($!nl-out)) !! self!failed('print-nl') } proto method slurp-rest(|) {*} multi method slurp-rest(IO::Handle:D: :$bin! where *.so, :$close --> Buf:D) { # NOTE: THIS METHOD WILL BE DEPRECATED IN 6.d in favour of .slurp() # Testing of it in roast master has been removed and only kept in 6.c # If you're changing this code for whatever reason, test with 6.c-errata LEAVE self.close if $close; my $res := nqp::create(buf8.^pun); loop { my $buf := self.read(0x100000); nqp::elems($buf) ?? $res.append($buf) !! return $res } } multi method slurp-rest(IO::Handle:D: :$enc, :$bin, :$close --> Str:D) { self!failed('slurp-rest') unless $!decoder; # NOTE: THIS METHOD WILL BE DEPRECATED IN 6.d in favour of .slurp() # Testing of it in roast master has been removed and only kept in 6.c # If you're changing this code for whatever reason, test with 6.c-errata LEAVE self.close if $close; self.encoding($enc) if $enc.defined; self!slurp-all-chars() } method slurp(IO::Handle:D: :$close, :$bin) { my $res; nqp::if( $!decoder, nqp::if( $bin, nqp::stmts( ($res := nqp::create(buf8.^pun)), nqp::if( $!decoder.bytes-available, $res.append( $!decoder.consume-exactly-bytes($!decoder.bytes-available) ) ) ), ($res := self!slurp-all-chars) ), ($res := nqp::create(buf8.^pun)) ); nqp::if( nqp::isfalse($!decoder) || $bin, nqp::while( nqp::elems(my $buf := self.READ(0x100000)), $res.append($buf) ) ); # don't sink result of .close; it might be a failed Proc #?if jvm nqp::stmts( nqp::if($close, my $ = self.close), $res ) #?endif #?if !jvm my $ = self.close if $close; $res #?endif } method !slurp-all-chars() { while nqp::elems(my $buf := self.READ(0x100000)) { $!decoder.add-bytes($buf); } $!decoder.consume-all-chars() } proto method spurt(|) {*} multi method spurt(IO::Handle:D: Blob $data, :$close) { LEAVE self.close if $close; self.WRITE($data); } multi method spurt(IO::Handle:D: Cool $data, :$close) { LEAVE self.close if $close; self.print($data); } method path(IO::Handle:D:) { $!path.IO } method IO(IO::Handle:D:) { $!path.IO } # use $.path, so IO::Pipe picks it up multi method Str(IO::Handle:D:) { $.path.Str } multi method gist(IO::Handle:D:) { "{self.^name}<$!path.gist()>({self.opened ?? 'opened' !! 'closed'})" } method flush(IO::Handle:D: --> True) { CATCH { default { fail X::IO::Flush.new: :os-error(.Str) } } nqp::defined($!PIO) or die 'File handle not open, so cannot flush'; nqp::flushfh($!PIO); } proto method encoding(|) {*} multi method encoding(IO::Handle:D:) { $!encoding // Nil } multi method encoding(IO::Handle:D: $new-encoding is copy, :$replacement, :$strict, Bool:D :$translate-nl = True) { with $new-encoding { if $_ eq 'bin' { $_ = Nil; } else { return $!encoding if $!decoder and $!encoding and $!encoding eq $_; } } with $!decoder { # We're switching encoding, or back to binary mode. First grab any # bytes the current decoder is holding on to but has not yet done # decoding of. my $available = $!decoder.bytes-available; with $new-encoding { my $prev-decoder := $!decoder; my $encoding = Encoding::Registry.find($new-encoding); $!decoder := $encoding.decoder(:$translate-nl, :$replacement, :$strict); $!decoder.set-line-separators($!nl-in.list); $!decoder.add-bytes($prev-decoder.consume-exactly-bytes($available)) if $available; $!encoder := $encoding.encoder(:$translate-nl, :$replacement, :$strict); $!encoding = $encoding.name; } else { nqp::seekfh($!PIO, -$available, SeekFromCurrent) if $available; $!decoder := Encoding::Decoder; $!encoder := Encoding::Encoder; $!encoding = Nil; Nil } } else { # No previous decoder; make a new one if needed, otherwise no change. with $new-encoding { my $encoding = Encoding::Registry.find($new-encoding); $!decoder := $encoding.decoder(:$translate-nl, :$replacement, :$strict); $!decoder.set-line-separators($!nl-in.list); $!encoder := $encoding.encoder(:$translate-nl, :$replacement, :$strict); $!encoding = $encoding.name; } else { Nil } } } submethod DESTROY(IO::Handle:D: --> Nil) { # Close handles with any file descriptor larger than 2. Those below # are our $*IN, $*OUT, and $*ERR, and we don't want them closed # implicitly via DESTROY, since you can't get them back again. self.close if nqp::defined($!PIO) # not closed yet && nqp::isgt_i(nqp::filenofh($!PIO),2) # not a standard handle #?if moar && nqp::not_i( # marked for closing nqp::isnull(nqp::atpos($opened,nqp::filenofh($!PIO))) ) #?endif } method native-descriptor(IO::Handle:D:) { nqp::defined($!PIO) ?? nqp::filenofh($!PIO) !! X::AdHoc.new( payload => 'File handle not open, so cannot get native descriptor' ).throw } } Rakudo::Internals.REGISTER-DYNAMIC: '$*DEFAULT-READ-ELEMS', { PROCESS::<$DEFAULT-READ-ELEMS> := %*ENV // 65536; } # vim: expandtab shiftwidth=4