Team Ai
Datasetpublic

MegaBites-AI/Windows-powershell

sourceHugging Facemitupdated 6mo agoView on Hugging Face
0likes372downloads
FileSystemContentStream.cs1504 linesDownload Raw Back to namespaces
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4using System;5using System.Collections;6using System.Collections.Generic;7using System.Diagnostics.CodeAnalysis;8using System.IO;9using System.Management.Automation;10using System.Management.Automation.Internal;11using System.Management.Automation.Provider;12using System.Runtime.CompilerServices;13using System.Runtime.InteropServices;14using System.Text;15using System.Threading.Tasks;16 17using Dbg = System.Management.Automation;18 19namespace Microsoft.PowerShell.Commands20{21    /// <summary>22    /// The content stream class for the file system provider. It implements both23    /// the IContentReader and IContentWriter interfaces.24    /// </summary>25    /// <remarks>26    /// Note, this class does no specific error handling. All errors are allowed to27    /// propagate to the caller so that they can be written to the error pipeline28    /// if necessary.29    /// </remarks>30    internal class FileSystemContentReaderWriter : IContentReader, IContentWriter31    {32        #region tracer33 34        /// <summary>35        /// An instance of the PSTraceSource class used for trace output36        /// using "FileSystemContentStream" as the category.37        /// </summary>38        [Dbg.TraceSource(39            "FileSystemContentStream",40            "The provider content reader and writer for the file system")]41        private static readonly Dbg.PSTraceSource s_tracer =42            Dbg.PSTraceSource.GetTracer("FileSystemContentStream",43            "The provider content reader and writer for the file system");44 45        #endregion tracer46 47        private readonly string _path;48        private readonly string _streamName;49        private readonly FileMode _mode;50        private readonly FileAccess _access;51        private readonly FileShare _share;52        private readonly Encoding _encoding;53        private readonly CmdletProvider _provider;54 55        private FileStream _stream;56        private StreamReader _reader;57        private StreamWriter _writer;58        private readonly bool _usingByteEncoding;59 60        private const char DefaultDelimiter = '\n';61 62        private readonly string _delimiter = $"{DefaultDelimiter}";63        private readonly int[] _offsetDictionary;64        private readonly bool _usingDelimiter;65        private readonly StringBuilder _currentLineContent;66        private bool _waitForChanges;67        private readonly bool _isRawStream;68        private long _fileOffset;69 70        private FileAttributes _oldAttributes;71        private bool _haveOldAttributes;72 73        // The reader to read file content backward74        private FileStreamBackReader _backReader;75        private bool _alreadyDetectEncoding = false;76 77        // False to add a newline to the end of the output string, true if not.78        private readonly bool _suppressNewline = false;79 80        /// <summary>81        /// Constructor for the content stream.82        /// </summary>83        /// <param name="path">84        /// The path to the file to get the content from.85        /// </param>86        /// <param name="mode">87        /// The file mode to open the file with.88        /// </param>89        /// <param name="access">90        /// The file access requested in the file.91        /// </param>92        /// <param name="share">93        /// The file share to open the file with94        /// </param>95        /// <param name="encoding">96        /// The encoding of the file to be read or written.97        /// </param>98        /// <param name="usingByteEncoding">99        /// If true, bytes will be read from the file. If false, the specified encoding100        /// will be used to read the file.101        /// </param>102        /// <param name="waitForChanges">103        /// If true, we will perform blocking reads on the file, waiting for new content to be appended104        /// </param>105        /// <param name="provider">106        /// The CmdletProvider invoking this stream107        /// </param>108        /// <param name="isRawStream">109        /// Indicates raw stream.110        /// </param>111        public FileSystemContentReaderWriter(112            string path,113            FileMode mode,114            FileAccess access,115            FileShare share,116            Encoding encoding,117            bool usingByteEncoding,118            bool waitForChanges,119            CmdletProvider provider,120            bool isRawStream)121            : this(122                path,123                streamName: null,124                mode,125                access,126                share,127                encoding,128                usingByteEncoding,129                waitForChanges,130                provider,131                isRawStream)132        {133        }134 135        /// <summary>136        /// Constructor for the content stream.137        /// </summary>138        /// <param name="path">139        /// The path to the file to get the content from.140        /// </param>141        /// <param name="streamName">142        /// The name of the Alternate Data Stream to get the content from. If null or empty, returns143        /// the file's primary content.144        /// </param>145        /// <param name="mode">146        /// The file mode to open the file with.147        /// </param>148        /// <param name="access">149        /// The file access requested in the file.150        /// </param>151        /// <param name="share">152        /// The file share to open the file with153        /// </param>154        /// <param name="encoding">155        /// The encoding of the file to be read or written.156        /// </param>157        /// <param name="usingByteEncoding">158        /// If true, bytes will be read from the file. If false, the specified encoding159        /// will be used to read the file.160        /// </param>161        /// <param name="waitForChanges">162        /// If true, we will perform blocking reads on the file, waiting for new content to be appended163        /// </param>164        /// <param name="provider">165        /// The CmdletProvider invoking this stream166        /// </param>167        /// <param name="isRawStream">168        /// Indicates raw stream.169        /// </param>170        public FileSystemContentReaderWriter(171            string path, string streamName, FileMode mode, FileAccess access, FileShare share,172            Encoding encoding, bool usingByteEncoding, bool waitForChanges, CmdletProvider provider,173            bool isRawStream)174        {175            if (string.IsNullOrEmpty(path))176            {177                throw PSTraceSource.NewArgumentNullException(nameof(path));178            }179 180            if (s_tracer.IsEnabled)181            {182                s_tracer.WriteLine("path = {0}", path);183                s_tracer.WriteLine("mode = {0}", mode);184                s_tracer.WriteLine("access = {0}", access);185            }186 187            _path = path;188            _streamName = streamName;189            _mode = mode;190            _access = access;191            _share = share;192            _encoding = encoding;193            _usingByteEncoding = usingByteEncoding;194            _waitForChanges = waitForChanges;195            _provider = provider;196            _isRawStream = isRawStream;197 198            CreateStreams(path, streamName, mode, access, share, encoding);199        }200 201        /// <summary>202        /// Constructor for the content stream.203        /// </summary>204        /// <param name="path">205        /// The path to the file to get the content from.206        /// </param>207        /// <param name="streamName">208        /// The name of the Alternate Data Stream to get the content from. If null or empty, returns209        /// the file's primary content.210        /// </param>211        /// <param name="mode">212        /// The file mode to open the file with.213        /// </param>214        /// <param name="access">215        /// The file access requested in the file.216        /// </param>217        /// <param name="share">218        /// The file share to open the file with219        /// </param>220        /// <param name="encoding">221        /// The encoding of the file to be read or written.222        /// </param>223        /// <param name="usingByteEncoding">224        /// If true, bytes will be read from the file. If false, the specified encoding225        /// will be used to read the file.226        /// </param>227        /// <param name="waitForChanges">228        /// If true, we will perform blocking reads on the file, waiting for new content to be appended229        /// </param>230        /// <param name="provider">231        /// The CmdletProvider invoking this stream232        /// </param>233        /// <param name="isRawStream">234        /// Indicates raw stream.235        /// </param>236        /// <param name="suppressNewline">237        /// False to add a newline to the end of the output string, true if not.238        /// </param>239        public FileSystemContentReaderWriter(240            string path, string streamName, FileMode mode, FileAccess access, FileShare share,241            Encoding encoding, bool usingByteEncoding, bool waitForChanges, CmdletProvider provider,242            bool isRawStream, bool suppressNewline)243                : this(path, streamName, mode, access, share, encoding, usingByteEncoding, waitForChanges, provider, isRawStream)244        {245            _suppressNewline = suppressNewline;246        }247 248        /// <summary>249        /// Constructor for the content stream.250        /// </summary>251        /// <param name="path">252        /// The path to the file to get the content from.253        /// </param>254        /// <param name="streamName">255        /// The name of the Alternate Data Stream to get the content from. If null or empty, returns256        /// the file's primary content.257        /// </param>258        /// <param name="mode">259        /// The file mode to open the file with.260        /// </param>261        /// <param name="access">262        /// The file access requested in the file.263        /// </param>264        ///  <param name="share">265        ///    The file share to open the file with266        ///  </param>267        /// <param name="delimiter">268        /// The delimiter to use when reading strings. Each time read is called, all contents up to an including269        /// the delimiter is read.270        /// </param>271        /// <param name="encoding">272        /// The encoding of the file to be read or written.273        /// </param>274        /// <param name="waitForChanges">275        /// If true, we will perform blocking reads on the file, waiting for new content to be appended276        /// </param>277        /// <param name="provider">278        /// The CmdletProvider invoking this stream279        /// </param>280        /// <param name="isRawStream">281        /// Indicates raw stream.282        /// </param>283        public FileSystemContentReaderWriter(284            string path,285            string streamName,286            FileMode mode,287            FileAccess access,288            FileShare share,289            string delimiter,290            Encoding encoding,291            bool waitForChanges,292            CmdletProvider provider,293            bool isRawStream)294            : this(path, streamName, mode, access, share, encoding, false, waitForChanges, provider, isRawStream)295        {296            // If the delimiter is default ('\n') we'll use ReadLine() method.297            // Otherwise allocate temporary structures for ReadDelimited() method.298            if (!(delimiter.Length == 1 && delimiter[0] == DefaultDelimiter))299            {300                _delimiter = delimiter;301                _usingDelimiter = true;302 303                // We expect that we are parsing files where line lengths can be relatively long.304                const int DefaultLineLength = 256;305                _currentLineContent = new StringBuilder(DefaultLineLength);306 307                // For Boyer-Moore string search algorithm.308                // Populate the offset lookups.309                // These will tell us the maximum number of characters310                // we can read to generate another possible match (safe shift).311                // If we read more characters than this, we risk consuming312                // more of the stream than we need.313                //314                // Because an unicode character size is 2 byte we would to have use315                // very large array with 65535 size to keep this safe offsets.316                // One solution is to pack unicode character to byte.317                // The workaround is to use low byte from unicode character.318                // This allow us to use small array with size 256.319                // This workaround is the fastest and provides excellent results320                // in regular search scenarios when the file contains321                // mostly characters from the same alphabet.322                _offsetDictionary = new int[256];323 324                // If next char from file is not in search pattern safe shift is the search pattern length.325                for (var n = 0; n < _offsetDictionary.Length; n++)326                {327                    _offsetDictionary[n] = _delimiter.Length;328                }329 330                // If next char from file is in search pattern we should calculate a safe shift.331                char currentChar;332                byte lowByte;333                for (var i = 0; i < _delimiter.Length; i++)334                {335                    currentChar = _delimiter[i];336                    lowByte = Unsafe.As<char, byte>(ref currentChar);337                    _offsetDictionary[lowByte] = _delimiter.Length - i - 1;338                }339            }340        }341 342        /// <summary>343        /// Reads the specified number of characters or a lines from the file.344        /// </summary>345        /// <param name="readCount">346        /// If less than 1, then the entire file is read at once. If 1 or greater, then347        /// readCount is used to determine how many items (ie: lines, bytes, delimited tokens)348        /// to read per call.349        /// </param>350        /// <returns>351        /// An array of strings representing the character(s) or line(s) read from352        /// the file.353        /// </returns>354        public IList Read(long readCount)355        {356            if (_isRawStream && _waitForChanges)357            {358                throw PSTraceSource.NewInvalidOperationException(FileSystemProviderStrings.RawAndWaitCannotCoexist);359            }360 361            bool waitChanges = _waitForChanges;362 363            s_tracer.WriteLine("blocks requested = {0}", readCount);364 365            var blocks = new List<object>();366            bool readToEnd = (readCount <= 0);367 368            if (_alreadyDetectEncoding && _reader.BaseStream.Position == 0)369            {370                Encoding curEncoding = _reader.CurrentEncoding;371                // Close the stream, and reopen the stream to make the BOM correctly processed.372                // The reader has already detected encoding, so if we don't reopen the stream, the BOM (if there is any)373                // will be treated as a regular character.374                _stream.Dispose();375                CreateStreams(_path, null, _mode, _access, _share, curEncoding);376                _alreadyDetectEncoding = false;377            }378 379            try380            {381                for (long currentBlock = 0; (currentBlock < readCount) || (readToEnd); ++currentBlock)382                {383                    if (waitChanges && _provider.Stopping)384                        waitChanges = false;385 386                    if (_usingByteEncoding)387                    {388                        if (!ReadByteEncoded(waitChanges, blocks, readBackward: false))389                            break;390                    }391                    else392                    {393                        if (_usingDelimiter || _isRawStream)394                        {395                            if (!ReadDelimited(waitChanges, blocks, readBackward: false, _delimiter))396                                break;397                        }398                        else399                        {400                            if (!ReadByLine(waitChanges, blocks, readBackward: false))401                                break;402                        }403                    }404                }405 406                s_tracer.WriteLine("blocks read = {0}", blocks.Count);407            }408            catch (Exception e)409            {410                if ((e is IOException) ||411                    (e is ArgumentException) ||412                    (e is System.Security.SecurityException) ||413                    (e is UnauthorizedAccessException) ||414                    (e is ArgumentNullException))415                {416                    // Exception contains specific message about the error occurred and so no need for errordetails.417                    _provider.WriteError(new ErrorRecord(e, "GetContentReaderIOError", ErrorCategory.ReadError, _path));418                    return null;419                }420                else421                    throw;422            }423 424            return blocks.ToArray();425        }426 427        /// <summary>428        /// Read the content regardless of the 'waitForChanges' flag.429        /// </summary>430        /// <param name="readCount"></param>431        /// <returns></returns>432        internal IList ReadWithoutWaitingChanges(long readCount)433        {434            bool oldWaitChanges = _waitForChanges;435            _waitForChanges = false;436            try437            {438                return Read(readCount);439            }440            finally441            {442                _waitForChanges = oldWaitChanges;443            }444        }445 446        /// <summary>447        /// Move the pointer of the stream to the position where there are 'backCount' number448        /// of items (depends on what we are using: delimiter? line? byts?) to the end of the file.449        /// </summary>450        /// <param name="backCount"></param>451        internal void SeekItemsBackward(int backCount)452        {453            if (backCount < 0)454            {455                // The caller needs to guarantee that 'backCount' is greater or equals to 0456                throw PSTraceSource.NewArgumentException(nameof(backCount));457            }458 459            if (_isRawStream && _waitForChanges)460            {461                throw PSTraceSource.NewInvalidOperationException(FileSystemProviderStrings.RawAndWaitCannotCoexist);462            }463 464            s_tracer.WriteLine("blocks seek backwards = {0}", backCount);465 466            var blocks = new List<object>();467            if (_reader != null)468            {469                // Make the reader automatically detect the encoding470                Seek(0, SeekOrigin.Begin);471                _reader.Peek();472                _alreadyDetectEncoding = true;473            }474 475            Seek(0, SeekOrigin.End);476 477            if (backCount == 0)478            {479                // If backCount is 0, we should move the position to the end of the file.480                // Maybe the "waitForChanges" is true in this case, which means that we are waiting for new inputs.481                return;482            }483 484            string actualDelimiter = string.Create(485                _delimiter.Length,486                _delimiter,487                (chars, buf) =>488                {489                    for (int i = 0, j = buf.Length - 1; i < chars.Length; i++, j--)490                    {491                        chars[i] = buf[j];492                    }493                });494 495            long currentBlock = 0;496            string lastDelimiterMatch = null;497 498            try499            {500                if (_isRawStream)501                {502                    // We always read to the end for the raw data.503                    // If it's indicated as RawStream, we move the pointer to the504                    // beginning of the file505                    Seek(0, SeekOrigin.Begin);506                    return;507                }508 509                for (; currentBlock < backCount; ++currentBlock)510                {511                    if (_usingByteEncoding)512                    {513                        if (!ReadByteEncoded(waitChanges: false, blocks, readBackward: true))514                            break;515                    }516                    else517                    {518                        if (_usingDelimiter)519                        {520                            if (!ReadDelimited(waitChanges: false, blocks, readBackward: true, actualDelimiter))521                                break;522                            // If the delimiter is at the end of the file, we need to read one more523                            // to get to the right position. For example:524                            //      ua123ua456ua -- -Tail 1525                            // If we read backward only once, we get 'ua', and cannot get to the right position526                            // So we read one more time, get 'ua456ua', and then we can get the right position527                            lastDelimiterMatch = (string)blocks[0];528                            if (currentBlock == 0 && lastDelimiterMatch.Equals(actualDelimiter, StringComparison.Ordinal))529                                backCount++;530                        }531                        else532                        {533                            if (!ReadByLine(waitChanges: false, blocks, readBackward: true))534                                break;535                        }536                    }537 538                    blocks.Clear();539                }540 541                // If usingByteEncoding is true, we don't create the reader and _backReader542                if (!_usingByteEncoding)543                {544                    long curStreamPosition = _backReader.GetCurrentPosition();545                    if (_usingDelimiter)546                    {547                        if (currentBlock == backCount)548                        {549                            Dbg.Diagnostics.Assert(lastDelimiterMatch != null, "lastDelimiterMatch should not be null when currentBlock == backCount");550                            if (lastDelimiterMatch.EndsWith(actualDelimiter, StringComparison.Ordinal))551                            {552                                curStreamPosition += _backReader.GetByteCount(_delimiter);553                            }554                        }555                    }556 557                    Seek(curStreamPosition, SeekOrigin.Begin);558                }559 560                s_tracer.WriteLine("blocks seek position = {0}", _stream.Position);561            }562            catch (Exception e)563            {564                if ((e is IOException) ||565                    (e is ArgumentException) ||566                    (e is System.Security.SecurityException) ||567                    (e is UnauthorizedAccessException) ||568                    (e is ArgumentNullException))569                {570                    // Exception contains specific message about the error occurred and so no need for errordetails.571                    _provider.WriteError(new ErrorRecord(e, "GetContentReaderIOError", ErrorCategory.ReadError, _path));572                }573                else574                    throw;575            }576        }577 578        private bool ReadByLine(bool waitChanges, List<object> blocks, bool readBackward)579        {580            // Reading lines as strings581            string line = readBackward ? _backReader.ReadLine() : _reader.ReadLine();582 583            if (line == null)584            {585                if (waitChanges)586                {587                    // We only wait for changes when read forwards. So here we use reader, instead of 'localReader'588                    do589                    {590                        WaitForChanges(_path, _mode, _access, _share, _reader.CurrentEncoding);591                        line = _reader.ReadLine();592                    }593                    while ((line == null) && (!_provider.Stopping));594                }595            }596 597            if (line != null)598            {599                blocks.Add(line);600            }601 602            int peekResult = readBackward ? _backReader.Peek() : _reader.Peek();603            if (peekResult == -1)604            {605                return false;606            }607            else608            {609                return true;610            }611        }612 613        private bool ReadDelimited(bool waitChanges, List<object> blocks, bool readBackward, string actualDelimiter)614        {615            if (_isRawStream)616            {617                // when -Raw is used we want to anyway read the whole thing618                // so avoiding the while loop by reading the entire content.619                string contentRead = _reader.ReadToEnd();620                if (contentRead.Length > 0)621                {622                    blocks.Add(contentRead);623                }624 625                // We already read whole file so return EOF.626                return false;627            }628 629            // Since the delimiter is a string, we're essentially630            // dealing with a "find the substring" algorithm, but with631            // the additional restriction that we cannot read past the632            // end of the delimiter. If we read past the end of the delimiter,633            // then we'll eat up bytes that we need from the filestream.634            // The solution is a modified Boyer-Moore string search algorithm.635            // This version retains the sub-linear search performance (via the636            // lookup tables).637            int numRead = 0;638            int currentOffset = actualDelimiter.Length;639            Span<char> readBuffer = stackalloc char[currentOffset];640            bool delimiterNotFound = true;641            _currentLineContent.Clear();642 643            do644            {645                // Read in the required batch of characters646                numRead = readBackward647                                ? _backReader.Read(readBuffer.Slice(0, currentOffset))648                                : _reader.Read(readBuffer.Slice(0, currentOffset));649 650                // If we want to wait for changes, then we'll keep on attempting to read651                // until we fill the buffer.652                if (numRead == 0)653                {654                    if (waitChanges)655                    {656                        // But stop reading if the provider is stopping657                        while ((numRead < currentOffset) && (!_provider.Stopping))658                        {659                            // Get the change, and try to read more characters660                            // We only wait for changes when read forwards, so here we don't need to check if 'readBackward' is661                            // true or false, we only use 'reader'. The member 'reader' will be updated by WaitForChanges.662                            WaitForChanges(_path, _mode, _access, _share, _reader.CurrentEncoding);663                            numRead += _reader.Read(readBuffer.Slice(0, currentOffset - numRead));664                        }665                    }666                }667 668                if (numRead > 0)669                {670                    _currentLineContent.Append(readBuffer.Slice(0, numRead));671 672                    // Look up the final character in our offset table.673                    // If the character doesn't exist in the lookup table, then it's not in674                    // our search key.  That means the match must happen strictly /after/ the675                    // current position.  Because of that, we can feel confident reading in the676                    // number of characters in the search key, without the risk of reading too many.677                    var currentChar = _currentLineContent[_currentLineContent.Length - 1];678                    currentOffset = _offsetDictionary[Unsafe.As<char, byte>(ref currentChar)];679 680                    // We want to keep reading if delimiter not found and we haven't hit the end of file681                    delimiterNotFound = true;682 683                    // If the final letters matched, then we will get an offset of "0".684                    // In that case, we'll either have a match (and break from the while loop,)685                    // or we need to move the scan forward one position.686                    if (currentOffset == 0)687                    {688                        currentOffset = 1;689 690                        if (actualDelimiter.Length <= _currentLineContent.Length)691                        {692                            delimiterNotFound = false;693                            int i = 0;694                            int j = _currentLineContent.Length - actualDelimiter.Length;695                            for (; i < actualDelimiter.Length; i++, j++)696                            {697                                if (actualDelimiter[i] != _currentLineContent[j])698                                {699                                    delimiterNotFound = true;700                                    break;701                                }702                            }703                        }704                    }705                }706            }707            while (delimiterNotFound && (numRead != 0));708 709            // We've reached the end of file or end of line.710            if (_currentLineContent.Length > 0)711            {712                // Add the block read to the output array list, trimming a trailing delimiter, if present.713                // Note: If -Tail was specified, we get here in the course of 2 distinct passes:714                //  - Once while reading backward simply to determine the appropriate *start position* for later forward reading, ignoring the content of the blocks read (in reverse).715                //  - Then again during forward reading, for regular output processing; it is only then that trimming the delimiter is necessary.716                //    (Trimming it during backward reading would not only be unnecessary, but could interfere with determining the correct start position.)717                blocks.Add(718                    !readBackward && !delimiterNotFound719                        ? _currentLineContent.ToString(0, _currentLineContent.Length - actualDelimiter.Length)720                        : _currentLineContent.ToString());721            }722 723            int peekResult = readBackward ? _backReader.Peek() : _reader.Peek();724            if (peekResult != -1)725            {726                return true;727            }728            else729            {730                if (readBackward && _currentLineContent.Length > 0)731                {732                    return true;733                }734 735                return false;736            }737        }738 739        private bool ReadByteEncoded(bool waitChanges, List<object> blocks, bool readBackward)740        {741            if (_isRawStream)742            {743                // if RawSteam, read all bytes and return. When RawStream is used, we dont744                // support -first, -last745                byte[] bytes = new byte[_stream.Length];746                int numBytesToRead = (int)_stream.Length;747                int numBytesRead = 0;748                while (numBytesToRead > 0)749                {750                    // Read may return anything from 0 to numBytesToRead.751                    int n = _stream.Read(bytes, numBytesRead, numBytesToRead);752 753                    // Break when the end of the file is reached.754                    if (n == 0)755                    {756                        break;757                    }758 759                    numBytesRead += n;760                    numBytesToRead -= n;761                }762 763                if (numBytesRead == 0)764                {765                    return false;766                }767                else768                {769                    blocks.Add(bytes);770                    return true;771                }772            }773 774            if (readBackward)775            {776                if (_stream.Position == 0)777                {778                    return false;779                }780 781                _stream.Position--;782                blocks.Add((byte)_stream.ReadByte());783                _stream.Position--;784                return true;785            }786 787            // Reading bytes not strings788            int byteRead = _stream.ReadByte();789 790            // We've found the end of the file.791            if (byteRead == -1)792            {793                // If we want to tail the file, wait for794                // the changes795                if (waitChanges)796                {797                    WaitForChanges(_path, _mode, _access, _share, Encoding.Default);798                    byteRead = _stream.ReadByte();799                }800            }801 802            // Add the byte we read to the list of blocks803            if (byteRead != -1)804            {805                blocks.Add((byte)byteRead);806                return true;807            }808            else809            {810                return false;811            }812        }813 814        private void CreateStreams(string filePath, string streamName, FileMode fileMode, FileAccess fileAccess, FileShare fileShare, Encoding fileEncoding)815        {816            // Try to mask off the ReadOnly, and Hidden attributes817            // if they've specified Force.818            if (File.Exists(filePath) && _provider.Force)819            {820                // Store the old attributes so that we can recover them821                // in the Close();822                _oldAttributes = File.GetAttributes(filePath);823                _haveOldAttributes = true;824 825                // Clear the hidden attribute, and if we're writing, also clear readonly.826                var attributesToClear = FileAttributes.Hidden;827                if ((fileAccess & (FileAccess.Write)) != 0)828                {829                    attributesToClear |= FileAttributes.ReadOnly;830                }831 832                File.SetAttributes(_path, (File.GetAttributes(filePath) & ~attributesToClear));833            }834 835            // If we want to write to the stream, attempt to open it for reading as well836            // so that we can determine the file encoding as we append to it837            FileAccess requestedAccess = fileAccess;838            if ((fileAccess & (FileAccess.Write)) != 0)839            {840                fileAccess = FileAccess.ReadWrite;841            }842 843            try844            {845#if !UNIX846                if (!string.IsNullOrEmpty(streamName))847                {848                    _stream = AlternateDataStreamUtilities.CreateFileStream(filePath, streamName, fileMode, fileAccess, fileShare);849                }850                else851#endif852                {853                    _stream = new FileStream(filePath, fileMode, fileAccess, fileShare);854                }855            }856            catch (IOException)857            {858#if !UNIX859                if (!string.IsNullOrEmpty(streamName))860                {861                    _stream = AlternateDataStreamUtilities.CreateFileStream(filePath, streamName, fileMode, requestedAccess, fileShare);862                }863                else864#endif865                {866                    _stream = new FileStream(filePath, fileMode, requestedAccess, fileShare);867                }868            }869 870            if (!_usingByteEncoding)871            {872                // Open the reader stream873                if ((fileAccess & (FileAccess.Read)) != 0)874                {875                    _reader = new StreamReader(_stream, fileEncoding);876                    _backReader = new FileStreamBackReader(_stream, fileEncoding);877                }878 879                // Open the writer stream880                if ((fileAccess & (FileAccess.Write)) != 0)881                {882                    // Ensure we are using the proper encoding883                    if ((_reader != null) &&884                        ((fileAccess & (FileAccess.Read)) != 0))885                    {886                        _reader.Peek();887                        fileEncoding = _reader.CurrentEncoding;888                    }889 890                    _writer = new StreamWriter(_stream, fileEncoding);891                }892            }893        }894 895        /// <summary>896        /// Waits for changes to the specified file.  To do this, it closes the file897        /// and then monitors for changes.  Once a change appears, it reopens the streams898        /// and seeks to the last read position.899        /// </summary>900        /// <param name="filePath">The path of the file to read / monitor.</param>901        /// <param name="fileMode">The FileMode of the file (ie: Open / Append).</param>902        /// <param name="fileAccess">The access properties of the file (ie: Read / Write).</param>903        /// <param name="fileShare">The sharing properties of the file (ie: Read / ReadWrite).</param>904        /// <param name="fileEncoding">The encoding of the file.</param>905        private void WaitForChanges(string filePath, FileMode fileMode, FileAccess fileAccess, FileShare fileShare, Encoding fileEncoding)906        {907            // Close the old stream, and store our current position.908            if (_stream != null)909            {910                _fileOffset = _stream.Position;911                _stream.Dispose();912            }913 914            // Watch for changes, as a blocking call.915            FileInfo watchFile = new FileInfo(filePath);916            long originalLength = watchFile.Length;917 918            using (FileSystemWatcher watcher = new FileSystemWatcher(watchFile.DirectoryName, watchFile.Name))919            {920                ErrorEventArgs errorEventArgs = null;921                var tcs = new TaskCompletionSource<FileSystemEventArgs>();922                FileSystemEventHandler onChangedHandler = (object source, FileSystemEventArgs e) => tcs.TrySetResult(e);923                RenamedEventHandler onRenamedHandler = (object source, RenamedEventArgs e) => tcs.TrySetResult(e);924                ErrorEventHandler onErrorHandler = (object source, ErrorEventArgs e) =>925                {926                    errorEventArgs = e;927                    tcs.TrySetResult(new FileSystemEventArgs(WatcherChangeTypes.All, watchFile.DirectoryName, watchFile.Name));928                };929 930                // With WaitForChanged, we registered for all change types, so we do the same here.931                watcher.Changed += onChangedHandler;932                watcher.Created += onChangedHandler;933                watcher.Deleted += onChangedHandler;934                watcher.Renamed += onRenamedHandler;935                watcher.Error += onErrorHandler;936 937                try938                {939                    watcher.EnableRaisingEvents = true;940 941                    while (!_provider.Stopping)942                    {943                        bool isTaskCompleted = tcs.Task.Wait(500);944 945                        if (errorEventArgs != null)946                        {947                            throw errorEventArgs.GetException();948                        }949 950                        if (isTaskCompleted)951                            break;952 953                        // If a process is still writing, .NET doesn't generate a change notification.954                        // So do a simple comparison on file size955                        watchFile.Refresh();956                        if (originalLength != watchFile.Length)957                        {958                            break;959                        }960                    }961                }962                finally963                {964                    // Done here to guarantee that the handlers are removed prior965                    // to the call to ManualResetEvent's Dispose() method.966                    watcher.EnableRaisingEvents = false;967                    watcher.Changed -= onChangedHandler;968                    watcher.Created -= onChangedHandler;969                    watcher.Deleted -= onChangedHandler;970                    watcher.Renamed -= onRenamedHandler;971                    watcher.Error -= onErrorHandler;972                }973            }974 975            // Let the change complete.976            // This is a fairly arbitrary number.  Without it, though,977            // some of the filesystem streams cannot be reopened.978            System.Threading.Thread.Sleep(100);979 980            // Reopen the streams.981            CreateStreams(filePath, null, fileMode, fileAccess, fileShare, fileEncoding);982 983            // If the file has been shortened, restart from zero.984            // That will let us catch log roll-overs.985            if (_fileOffset > _stream.Length)986                _fileOffset = 0;987 988            // Seek to the place we last left off.989            _stream.Seek(_fileOffset, SeekOrigin.Begin);990            _reader?.DiscardBufferedData();991            _backReader?.DiscardBufferedData();992        }993 994        /// <summary>995        /// Moves the current stream position in the file.996        /// </summary>997        /// <param name="offset">998        /// The offset from the origin to move the position to.999        /// </param>1000        /// <param name="origin">1001        /// The origin from which the offset is calculated.1002        /// </param>1003        public void Seek(long offset, SeekOrigin origin)1004        {1005            _writer?.Flush();1006 1007            _stream.Seek(offset, origin);1008 1009            _writer?.Flush();1010            _reader?.DiscardBufferedData();1011            _backReader?.DiscardBufferedData();1012        }1013 1014        /// <summary>1015        /// Closes the file.1016        /// </summary>1017        public void Close()1018        {1019            bool streamClosed = false;1020 1021            if (_writer != null)1022            {1023                try1024                {1025                    _writer.Flush();1026                    _writer.Dispose();1027                }1028                finally1029                {1030                    streamClosed = true;1031                }1032            }1033 1034            if (_reader != null)1035            {1036                _reader.Dispose();1037                streamClosed = true;1038            }1039 1040            if (_backReader != null)1041            {1042                _backReader.Dispose();1043                streamClosed = true;1044            }1045 1046            if (!streamClosed)1047            {1048                _stream.Flush();1049                _stream.Dispose();1050            }1051 1052            // Reset the attributes1053            if (_haveOldAttributes && _provider.Force)1054            {1055                File.SetAttributes(_path, _oldAttributes);1056            }1057        }1058 1059        /// <summary>1060        /// Writes the specified object to the file.1061        /// </summary>1062        /// <param name="content">1063        /// The objects to write to the file1064        /// </param>1065        /// <returns>1066        /// The objects written to the file.1067        /// </returns>1068        public IList Write(IList content)1069        {1070            foreach (object line in content)1071            {1072                object[] contentArray = line as object[];1073                if (contentArray != null)1074                {1075                    foreach (object obj in contentArray)1076                    {1077                        WriteObject(obj);1078                    }1079                }1080                else1081                {1082                    WriteObject(line);1083                }1084            }1085 1086            return content;1087        }1088 1089        private void WriteObject(object content)1090        {1091            if (content == null)1092            {1093                return;1094            }1095 1096            if (_usingByteEncoding)1097            {1098                try1099                {1100                    byte byteToWrite = (byte)content;1101 1102                    _stream.WriteByte(byteToWrite);1103                }1104                catch (InvalidCastException)1105                {1106                    throw PSTraceSource.NewArgumentException(nameof(content), FileSystemProviderStrings.ByteEncodingError);1107                }1108            }1109            else1110            {1111                if (_suppressNewline)1112                {1113                    _writer.Write(content.ToString());1114                }1115                else1116                {1117                    _writer.WriteLine(content.ToString());1118                }1119            }1120        }1121 1122        /// <summary>1123        /// Closes the file stream.1124        /// </summary>1125        public void Dispose()1126        {1127            Dispose(true);1128            GC.SuppressFinalize(this);1129        }1130 1131        internal void Dispose(bool isDisposing)1132        {1133            if (isDisposing)1134            {1135                _stream?.Dispose();1136                _reader?.Dispose();1137                _backReader?.Dispose();1138                _writer?.Dispose();1139            }1140        }1141    }1142 1143    internal sealed class FileStreamBackReader : StreamReader1144    {1145        internal FileStreamBackReader(FileStream fileStream, Encoding encoding)1146            : base(fileStream, encoding)1147        {1148            _stream = fileStream;1149            if (_stream.Length > 0)1150            {1151                long curPosition = _stream.Position;1152                _stream.Seek(0, SeekOrigin.Begin);1153                base.Peek();1154                _stream.Position = curPosition;1155                _currentEncoding = base.CurrentEncoding;1156                _currentPosition = _stream.Position;1157 1158                // Get the oem encoding and system current ANSI code page1159                _oemEncoding = EncodingConversion.Convert(null, EncodingConversion.OEM);1160                _defaultAnsiEncoding = EncodingConversion.Convert(null, EncodingConversion.Default);1161            }1162        }1163 1164        private readonly FileStream _stream;1165        private readonly Encoding _currentEncoding;1166        private readonly Encoding _oemEncoding;1167        private readonly Encoding _defaultAnsiEncoding;1168 1169        private const int BuffSize = 4096;1170 1171        private readonly byte[] _byteBuff = new byte[BuffSize];1172        private readonly char[] _charBuff = new char[BuffSize];1173        private int _byteCount = 0;1174        private int _charCount = 0;1175        private long _currentPosition = 0;1176        private const byte BothTopBitsSet = 0xC0;1177        private const byte TopBitUnset = 0x80;1178 1179        /// <summary>1180        /// We don't support this method because it is not used by the ReadBackward method in FileStreamContentReaderWriter.1181        /// </summary>1182        /// <param name="buffer"></param>1183        /// <param name="index"></param>1184        /// <param name="count"></param>1185        /// <returns></returns>1186        public override int ReadBlock(char[] buffer, int index, int count)1187        {1188            // This method is not supposed to be used1189            throw PSTraceSource.NewNotSupportedException();1190        }1191 1192        /// <summary>1193        /// We don't support this method because it is not used by the ReadBackward method in FileStreamContentReaderWriter.1194        /// </summary>1195        /// <returns></returns>1196        public override string ReadToEnd()1197        {1198            // This method is not supposed to be used1199            throw PSTraceSource.NewNotSupportedException();1200        }

Showing the first 1,200 of 1504 lines. Download the file for the rest.