/** ****************************************************************************************
         * Cuts the given number of characters from the end of the Substring and optionally
         * places the portion that was cut in parameter \p target (if provided).<br>
         * Parameter \p regionLength is checked to be between 0 and length. If negative, nothing
         * is cut and \p target is set empty. If \p regionLength is greater than this
         * objects' length, all contents is 'moved' to \p target.
         *
         * @param regionLength  The length of the region at the start to delete.
         * @param target        An optional target \b %Substring that receives the portion that
         *                      is cut from this object. Defaults to null.
         *
         * @return The new length of the substring.
         ******************************************************************************************/
        public int        ConsumeFromEnd( int regionLength,  Substring target= null )
        {
            if ( regionLength < 0 )
            {
                if ( target != null )
                    target.Clear();
                return  Length();
            }
            if ( regionLength > Length() )
                regionLength= Length();

            if ( target != null )
                target.Set( this, Length() - regionLength, regionLength );

            End-= regionLength;
            hash= 0;
            return Length();
        }
    // #############################################################################################
    // file IO
    // #############################################################################################

        /** ****************************************************************************************
         * Clears all configuration data and reads the file. It might happen that lines are
         * ignored or otherwise marked as faulty. All numbers of such lines get collected in
         * field LinesWithReadErrors.
         * @return Returns the #Status of the operation.
         ******************************************************************************************/
        public IniFile.Status  ReadFile()
        {
            Reset();
            LastStatus= Status.OK;

            // read all variables
            StreamReader file;
            try
            {
                file= new StreamReader( FileName.ToString() );
            }
            catch( Exception )
            {
                return LastStatus= Status.ERROR_OPENING_FILE;
            }

            String      lineS;
            AString     name=       new AString();
            AString     value=      new AString();
            AString     comments=   new AString();
            Section     actSection= (IniFile.Section) Sections[0];
            Substring   line=       new Substring();

            int         lineNo= 0;
            bool        fileHeaderRead= false;

            char[]      separatorCharacters= value._( "=" )._( CString.DefaultWhitespaces )
                                                  .ToString().ToCharArray();

            LinesWithReadErrors.Clear();
            while ( (lineS= file.ReadLine()) != null )
            {
                lineNo++;

                bool isEmpty=       line.Set( lineS ).Trim().IsEmpty();
                bool isCommentLine= startsWithCommentSymbol( line );

                if ( isCommentLine )
                {
                    if ( comments.IsNotEmpty() )
                        comments.NewLine();
                    comments._(line);
                    continue;
                }

                // still processing file header?
                if ( !fileHeaderRead )
                {
                    fileHeaderRead= true;
                    FileComments._()._( comments );
                    comments.Clear();
                }

                // empty line?
                if ( isEmpty )
                {
                    if ( comments.IsNotEmpty() )
                        comments.NewLine();
                    continue;
                }

                // section line
                if ( line.Consume( '[' ) )
                {
                    fileHeaderRead= true;

                    // we do not care if there is no closing bracket. But if there is one, we remove it.
                    if( !line.ConsumeFromEnd(']') )
                        LinesWithReadErrors.Add( lineNo );

                    // search the section in our section list (if section existed already, new comments
                    // are dropped)
                    actSection= (IniFile.Section) SearchOrCreateSection( line, comments);
                    comments.Clear();

                    continue;
                }

                // variable line?
                value.Clear();
                int idx= line.IndexOfAny( separatorCharacters, Inclusion.Include );
                if( idx < 0 )
                {
                    name._()._( line );
                    line.Clear();
                }
                else
                {
                    name._()._( line.Buf, line.Start, idx );
                    line.Consume( idx );
                    value._(line);
                }

                // read continues as long as lines end with '\' (must not be '\\')
                while (     line.CharAtEnd()  == '\\'
                        &&  line.CharAtEnd(1) != '\\' )
                {
                    value.NewLine();

                    if ( (lineS= file.ReadLine()) == null  )
                    {
                        // last line of the file ended with '\' !
                        line.Clear();
                        break;
                    }
                    line.Set( lineS ).TrimEnd();
                    value._( line );
                }

                // insert entry with raw value
                {
                    IniFile.Entry entry= (IniFile.Entry) actSection.GetEntry( name, true );
                    entry.Values  .Clear();
                    entry.Comments._()._( comments );
                    entry.RawValue._()._( value );

                    // if there is just no raw value, we add an empty string to the entries' values
                    if ( value.IsEmpty() )
                        entry.Values.Add( new AString() );
                }

                comments.Clear();

            }
            file.Close();

            return LastStatus;
        }
        /** ****************************************************************************************
         * Cuts the given number of characters from the beginning of the Substring and optionally
         * places the portion that was cut in parameter \p target (if provided).<br>
         * Parameter \p regionLength is checked to be between 0 and length. If negative, nothing
         * is cut and \p target is set empty. If \p regionLength is greater than this
         * objects' length, all contents is 'moved' to \p target.
         *
         * @param regionLength  The length of the region at the start to delete.
         * @param target        An optional target \b %Substring that receives the portion that
         *                      is cut from this object. Defaults to null.
         *
         * @return The new length of the substring.
         ******************************************************************************************/
        public int        Consume( int regionLength,  Substring target= null )
        {
            if ( regionLength < 0 )
            {
                if ( target != null )
                    target.Clear();
                return  Length();
            }
            if ( regionLength > Length() )
                regionLength= Length();

            if ( target != null )
                target.Set( this, 0, regionLength );

            Start+= regionLength;
            hash= 0;
            return Length();
        }