2007-04-17 Everaldo Canuto <everaldo@simios.org>
[mono.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / Mime.cs
index fb7839abd048830ce3021607a4a69f2c8c065316..398701e779174cb7f0dbdd6b20611e0749bc9d79 100644 (file)
 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 //
-// Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
+// Copyright (c) 2006 Alexander Olk
 //
 // Authors:
 //
-//  Alexander Olk      xenomorph2@onlinehome.de
+//  Alexander Olk      alex.olk@googlemail.com
 //
 
 using System;
@@ -47,13 +47,15 @@ using System.Text;
 //   string[] available = Mime.AvailableMimeTypes;
 
 // TODO:
-// - optimize
-// - little/big endian stuff for TypeHostXX
+// - optimize even more :)
 // - async callback ?!?
 // - freedesktop org file extensions can have regular expressions also, resolve them too
-// - sort match collections by magic priority ( higher = first )
-// - MimeGenerated: use indexes to point to mime type name strings instead of repeating the name string each time (in match, subclass, etc.) !?!
-// - buffer is currently hard coded to size 8192, value should be determined by MimeGenerated
+// - sort match collections by magic priority ( higher = first ) ?
+
+// internal test:
+// looking up the mime types 20 times for 2757 files in /usr/lib without caching (mime_file_cache)
+// old version: Time: 00:00:32.3791220
+// new version: Time: 00:00:16.9991810
 
 namespace System.Windows.Forms
 {
@@ -66,7 +68,7 @@ namespace System.Windows.Forms
                
                private FileStream file_stream;
                
-               private byte[] buffer = new byte[ 8192 ];
+               private byte[] buffer = null;
                
                private const string octet_stream = "application/octet-stream";
                private const string text_plain = "text/plain";
@@ -74,217 +76,185 @@ namespace System.Windows.Forms
                
                private StringDictionary mime_file_cache = new StringDictionary();
                
-               private const int mime_file_cache_max_size = 5000;
+               private const int mime_file_cache_max_size = 3000;
                
                private string search_string;
                
                private static object lock_object = new Object();
                
-               private int platform = (int) Environment.OSVersion.Platform;
-               
                private bool is_zero_file = false;
                
-               public Mime( )
+               private int bytes_read = 0;
+               
+               private bool mime_available = false;
+               
+               public static NameValueCollection Aliases;
+               public static NameValueCollection SubClasses;
+               
+               public static NameValueCollection GlobalPatternsShort;
+               public static NameValueCollection GlobalPatternsLong;
+               public static NameValueCollection GlobalLiterals;
+               public static NameValueCollection GlobalSufPref;
+               
+               public static ArrayList Matches80Plus;
+               public static ArrayList MatchesBelow80;
+               
+               private Mime ()
                {
-                       MimeGenerated.Init( );
+#if NET_2_0
+                       Aliases = new NameValueCollection (StringComparer.CurrentCultureIgnoreCase);
+                       SubClasses = new NameValueCollection (StringComparer.CurrentCultureIgnoreCase);
+                       GlobalPatternsShort = new NameValueCollection (StringComparer.CurrentCultureIgnoreCase);
+                       GlobalPatternsLong = new NameValueCollection (StringComparer.CurrentCultureIgnoreCase);
+                       GlobalLiterals = new NameValueCollection (StringComparer.CurrentCultureIgnoreCase);
+                       GlobalSufPref = new NameValueCollection (StringComparer.CurrentCultureIgnoreCase);
+#else
+                       Aliases = new NameValueCollection (new CaseInsensitiveHashCodeProvider (), new Comparer (System.Globalization.CultureInfo.CurrentUICulture));
+                       SubClasses = new NameValueCollection (new CaseInsensitiveHashCodeProvider (), new Comparer (System.Globalization.CultureInfo.CurrentUICulture));
+                       GlobalPatternsShort = new NameValueCollection (new CaseInsensitiveHashCodeProvider (), new Comparer (System.Globalization.CultureInfo.CurrentUICulture));
+                       GlobalPatternsLong = new NameValueCollection (new CaseInsensitiveHashCodeProvider (), new Comparer (System.Globalization.CultureInfo.CurrentUICulture));
+                       GlobalLiterals = new NameValueCollection (new CaseInsensitiveHashCodeProvider (), new Comparer (System.Globalization.CultureInfo.CurrentUICulture));
+                       GlobalSufPref = new NameValueCollection (new CaseInsensitiveHashCodeProvider (), new Comparer (System.Globalization.CultureInfo.CurrentUICulture));
+#endif
+                       
+                       Matches80Plus = new ArrayList ();
+                       MatchesBelow80 = new ArrayList ();
+                       
+                       FDOMimeConfigReader fmcr = new FDOMimeConfigReader ();
+                       int buffer_length = fmcr.Init ();
                        
-//                     Console.WriteLine( "Mime Instance created..." );
+                       if (buffer_length != -1) {
+                               buffer = new byte [buffer_length];
+                               mime_available = true;
+                       }
+               }
+               
+               public static bool MimeAvailable {
+                       get {
+                               return Instance.mime_available;
+                       }
                }
                
-               public static string GetMimeTypeForFile( string filename )
+               public static string GetMimeTypeForFile (string filename)
                {
-                       lock ( lock_object )
-                       {
-                               Instance.StartByFileName( filename );
+                       lock (lock_object) {
+                               Instance.StartByFileName (filename);
                        }
                        
                        return Instance.global_result;
                }
                
                // not tested
-               public static string GetMimeTypeForData( byte[] data )
+               public static string GetMimeTypeForData (byte[] data)
                {
-                       lock ( lock_object )
-                       {
-                               Instance.StartDataLookup( data );
+                       lock (lock_object) {
+                               Instance.StartDataLookup (data);
                        }
                        
                        return Instance.global_result;
                }
                
-               public static string GetMimeTypeForString( string input )
+               public static string GetMimeTypeForString (string input)
                {
-                       lock ( lock_object )
-                       {
-                               Instance.StartStringLookup( input );
+                       lock (lock_object) {
+                               Instance.StartStringLookup (input);
                        }
                        
                        return Instance.global_result;
                }
                
-               public static string GetMimeAlias( string mimetype )
+               public static string GetMimeAlias (string mimetype)
                {
-                       return MimeGenerated.Aliases[ mimetype ];
+                       return Aliases [mimetype];
                }
                
-               public static string GetMimeSubClass( string mimetype )
+               public static string GetMimeSubClass (string mimetype)
                {
-                       return MimeGenerated.SubClasses[ mimetype ];
+                       return SubClasses [mimetype];
                }
                
-               public static string[] AvailableMimeTypes
+               public static void CleanFileCache ()
                {
-                       get {
-                               string[] result = new string[ MimeGenerated.MimeTypes.Count ];
-                               
-                               MimeGenerated.MimeTypes.Keys.CopyTo( result, 0 );
-                               
-                               return result;
+                       lock (lock_object) {
+                               Instance.mime_file_cache.Clear ();
                        }
                }
                
-               private void StartByFileName( string filename )
+               private void StartByFileName (string filename)
                {
-                       if ( mime_file_cache.ContainsKey( filename ) )
-                       {
-                               global_result = mime_file_cache[ filename ];
+                       if (mime_file_cache.ContainsKey (filename)) {
+                               global_result = mime_file_cache [filename];
                                return;
                        }
                        
                        current_file_name = filename;
                        is_zero_file = false;
                        
-                       if ( !CheckForInode( ) )
-                       {
-                               global_result = octet_stream;
-                               
-                               GoByFileName( );
-                       }
+                       global_result = octet_stream;
+                       
+                       GoByFileName ();
                        
-                       if ( !mime_file_cache.ContainsKey( current_file_name ) )
-                               mime_file_cache.Add( current_file_name, global_result );
+                       mime_file_cache.Add (current_file_name, global_result);
                        
-                       // not tested
-                       if ( mime_file_cache.Count > mime_file_cache_max_size )
-                       {
-                               IEnumerator enumerator = mime_file_cache.GetEnumerator( );
+                       if (mime_file_cache.Count > mime_file_cache_max_size) {
+                               IEnumerator enumerator = mime_file_cache.GetEnumerator ();
+                               
+                               int counter = mime_file_cache_max_size - 500;
                                
-                               for ( int i = 0; i < mime_file_cache_max_size - 1000; i++ )
-                               {
-                                       mime_file_cache.Remove( enumerator.Current.ToString( ) );
+                               while (enumerator.MoveNext ()) {
+                                       mime_file_cache.Remove (enumerator.Current.ToString ());
+                                       counter--;
+                                       
+                                       if (counter == 0)
+                                               break;
                                }
                        }
                }
                
-               private void StartDataLookup( byte[] data )
+               private void StartDataLookup (byte[] data)
                {
                        global_result = octet_stream;
                        
-                       System.Array.Clear( buffer, 0, buffer.Length );
+                       System.Array.Clear (buffer, 0, buffer.Length);
                        
-                       if ( data.Length > buffer.Length )
-                       {
-                               System.Array.Copy( data, buffer, buffer.Length );
-                       }
-                       else
-                       {
-                               System.Array.Copy( data, buffer, data.Length );
+                       if (data.Length > buffer.Length) {
+                               System.Array.Copy (data, buffer, buffer.Length);
+                       } else {
+                               System.Array.Copy (data, buffer, data.Length);
                        }
                        
-                       if ( CheckMatch80Plus( ) )
+                       if (CheckMatch80Plus ())
                                return;
                        
-                       if ( CheckMatchBelow80( ) )
+                       if (CheckMatchBelow80 ())
                                return;
                        
-                       CheckForBinaryOrText);
+                       CheckForBinaryOrText ();
                }
                
-               private void StartStringLookup( string input )
+               private void StartStringLookup (string input)
                {
                        global_result = text_plain;
                        
                        search_string = input;
                        
-                       if ( CheckForContentTypeString( ) )
+                       if (CheckForContentTypeString ())
                                return;
                }
                
-               private bool CheckForInode( )
-               {
-                       if ( ( platform == 4 ) || ( platform == 128 ) )
-                       {
-#if __MonoCS__
-                               // *nix platform
-                               Mono.Unix.UnixFileInfo ufi = new Mono.Unix.UnixFileInfo( current_file_name );
-                               
-                               if ( ufi.IsFile )
-                               {
-                                       return false;
-                               }
-                               else
-                               if ( ufi.IsDirectory )
-                               {
-                                       global_result = "inode/directory";
-                                       return true;
-                               }
-                               else
-                               if ( ufi.IsBlockDevice )
-                               {
-                                       global_result = "inode/blockdevice";
-                                       return true;
-                               }
-                               else
-                               if ( ufi.IsSocket )
-                               {
-                                       global_result = "inode/socket";
-                                       return true;
-                               }
-                               else
-                               if ( ufi.IsSymbolicLink )
-                               {
-                                       global_result = "inode/symlink";
-                                       return true;
-                               }
-                               else
-                               if ( ufi.IsCharacterDevice )
-                               {
-                                       global_result = "inode/chardevice";
-                                       return true;
-                               }
-                               else
-                               if ( ufi.IsFIFO )
-                               {
-                                       global_result = "inode/fifo";
-                                       return true;
-                               }
-#endif
-                       }
-                       else
-                       {
-                               // TODO!!!!
-                               // windows platform
-                       }
-                       
-                       return false;
-               }
-               
-               private void GoByFileName( )
+               private void GoByFileName ()
                {
                        // check if we can open the file
-                       if ( !OpenFile( ) )
-                       {
+                       if (!OpenFile ()) {
                                // couldn't open the file, check globals only
-                               
-                               CheckGlobalPatterns( );
+                               CheckGlobalPatterns ();
                                
                                return;
                        }
                        
-                       if ( !is_zero_file )
-                       {
+                       if (!is_zero_file) {
                                // check for matches with a priority >= 80
-                               if ( CheckMatch80Plus( ) )
+                               if (CheckMatch80Plus ())
                                        return;
                        }
                        
@@ -292,27 +262,25 @@ namespace System.Windows.Forms
                        // this should be done for zero size files also,
                        // for example zero size file trash.ccc~ should return
                        // application/x-trash instead of application/x-zerosize
-                       if ( CheckGlobalPatterns( ) )
+                       if (CheckGlobalPatterns ())
                                return;
                        
                        // if file size is zero, no other checks are needed
-                       if ( is_zero_file )
+                       if (is_zero_file)
                                return;
                        
                        // ok, still nothing matches then try matches with a priority < 80
-                       if ( CheckMatchBelow80( ) )
+                       if (CheckMatchBelow80 ())
                                return;
                        
                        // wow, still nothing... return application/octet-stream for binary data, or text/plain for textual data
-                       CheckForBinaryOrText);
+                       CheckForBinaryOrText ();
                }
                
-               private bool CheckMatch80Plus)
+               private bool CheckMatch80Plus ()
                {
-                       foreach ( Match match in MimeGenerated.Matches80Plus )
-                       {
-                               if ( TestMatch( match ) )
-                               {
+                       foreach (Match match in Matches80Plus) {
+                               if (TestMatch (match)) {
                                        global_result = match.MimeType;
                                        
                                        return true;
@@ -322,120 +290,159 @@ namespace System.Windows.Forms
                        return false;
                }
                
-               private void CheckGlobalResult( )
+               // this little helper method gives us a real speed improvement
+               private bool FastEndsWidth (string input, string value)
                {
-                       int comma_index = global_result.IndexOf( "," );
+                       if (value.Length > input.Length)
+                               return false;
                        
-                       if ( comma_index != -1 )
-                       {
-                               global_result = global_result.Substring( 0, comma_index );
+                       int z = input.Length - 1;
+                       
+                       for (int i = value.Length - 1; i > -1; i--) {
+                               if (value [i] != input [z])
+                                       return false;
+                               
+                               z--;
                        }
+                       
+                       return true;
+               }
+               
+               private bool FastStartsWith (string input, string value)
+               {
+                       if (value.Length > input.Length)
+                               return false;
+                       
+                       for (int i = 0; i < value.Length; i++)
+                               if (value [i] != input [i])
+                                       return false;
+                       
+                       return true;
                }
                
-               private bool CheckGlobalPatterns( )
+               // start always with index = 0
+               private int FastIndexOf (string input, char value)
                {
-                       string filename = Path.GetFileName( current_file_name );
-                       string filename_lower = filename.ToLower( );
+                       if (input.Length == 0)
+                               return -1;
                        
-                       // first check for literals
+                       for (int i = 0; i < input.Length; i++)
+                               if (input [i] == value)
+                                       return i;
                        
-                       for ( int i = 0; i < MimeGenerated.GlobalLiterals.Count; i++ )
-                       {
-                               string key = MimeGenerated.GlobalLiterals.GetKey( i );
+                       return -1;
+               }
+               
+               private int FastIndexOf (string input, string value)
+               {
+                       if (input.Length == 0)
+                               return -1;
+                       
+                       for (int i = 0; i < input.Length - value.Length; i++) {
+                               if (input [i] == value [0]) {
+                                       int counter = 0;
+                                       for (int z = 1; z < value.Length; z++) {
+                                               if (input [i + z] != value [z])
+                                                       break;
+                                               
+                                               counter++;
+                                       }
+                                       if (counter == value.Length - 1) {
+                                               return i;
+                                       }
+                               }
+                       }
+                       
+                       return -1;
+               }
+               
+               private void CheckGlobalResult ()
+               {
+                       int comma_index = FastIndexOf (global_result, ',');
+                       
+                       if (comma_index != -1) {
+                               global_result = global_result.Substring (0, comma_index);
+                       }
+               }
+               
+               private bool CheckGlobalPatterns ()
+               {
+                       string filename = Path.GetFileName (current_file_name);
+                       
+                       // first check for literals
+                       for (int i = 0; i < GlobalLiterals.Count; i++) {
+                               string key = GlobalLiterals.GetKey (i);
                                
                                // no regex char
-                               if ( key.IndexOf( '[' ) == -1 )
-                               {
-                                       if ( key.Equals( filename ) )
-                                       {
-                                               global_result = MimeGenerated.GlobalLiterals[ i ];
-                                               CheckGlobalResult( );
+                               if (FastIndexOf (key, '[') == -1) {
+                                       if (FastIndexOf (filename, key) != -1) {
+                                               global_result = GlobalLiterals [i];
+                                               CheckGlobalResult ();
                                                return true;
                                        }
-                               }
-                               else // regex it ;)
-                               {
-                                       if ( Regex.IsMatch( filename, key ) )
-                                       {
-                                               global_result = MimeGenerated.GlobalLiterals[ i ];
-                                               CheckGlobalResult( );
+                               } else {
+                                       if (Regex.IsMatch (filename, key)) {
+                                               global_result = GlobalLiterals [i];
+                                               CheckGlobalResult ();
                                                return true;
                                        }
                                }
                        }
                        
-                       if ( filename.IndexOf( '.' ) != -1 )
-                       {
+                       if (FastIndexOf (filename, '.') != -1) {
                                // check for double extension like .tar.gz
-                               
-                               for ( int i = 0; i < MimeGenerated.GlobalPatternsLong.Count; i++ )
-                               {
-                                       string key = MimeGenerated.GlobalPatternsLong.GetKey( i );
+                               for (int i = 0; i < GlobalPatternsLong.Count; i++) {
+                                       string key = GlobalPatternsLong.GetKey (i);
                                        
-                                       if ( filename.EndsWith( key ) )
-                                       {
-                                               global_result = MimeGenerated.GlobalPatternsLong[ i ];
-                                               CheckGlobalResult( );
+                                       if (FastEndsWidth (filename, key)) {
+                                               global_result = GlobalPatternsLong [i];
+                                               CheckGlobalResult ();
                                                return true;
-                                       }
-                                       else
-                                       {
-                                               if ( filename_lower.EndsWith( key ) )
-                                               {
-                                                       global_result = MimeGenerated.GlobalPatternsLong[ i ];
-                                                       CheckGlobalResult( );
+                                       } else {
+                                               if (FastEndsWidth (filename.ToLower (), key)) {
+                                                       global_result = GlobalPatternsLong [i];
+                                                       CheckGlobalResult ();
                                                        return true;
                                                }
                                        }
                                }
                                
                                // check normal extensions...
+                               string extension = Path.GetExtension (current_file_name);
                                
-                               string extension = Path.GetExtension( current_file_name );
-                               
-                               if ( extension.Length != 0 )
-                               {
-                                       global_result = MimeGenerated.GlobalPatternsShort[ extension ];
+                               if (extension.Length != 0) {
+                                       string global_result_tmp = GlobalPatternsShort [extension];
                                        
-                                       if ( global_result != null )
-                                       {
-                                               CheckGlobalResult);
+                                       if (global_result_tmp != null) {
+                                               global_result = global_result_tmp;
+                                               CheckGlobalResult ();
                                                return true;
                                        }
                                        
-                                       string extension_lower = extension.ToLower( );
-                                       
-                                       global_result = MimeGenerated.GlobalPatternsShort[ extension_lower ];
+                                       global_result_tmp = GlobalPatternsShort [extension.ToLower ()];
                                        
-                                       if ( global_result != null )
-                                       {
-                                               CheckGlobalResult);
+                                       if (global_result_tmp != null) {
+                                               global_result = global_result_tmp;
+                                               CheckGlobalResult ();
                                                return true;
                                        }
                                }
                        }
                        
                        // finally check if a prefix or suffix matches
-                       
-                       for ( int i = 0; i < MimeGenerated.GlobalSufPref.Count; i++ )
-                       {
-                               string key = MimeGenerated.GlobalSufPref.GetKey( i );
+                       for (int i = 0; i < GlobalSufPref.Count; i++) {
+                               string key = GlobalSufPref.GetKey (i);
                                
-                               if ( key.StartsWith( "*" ) )
-                               {
-                                       if ( filename.EndsWith( key.Replace( "*", "" ) ) )
-                                       {
-                                               global_result = MimeGenerated.GlobalSufPref[ i ];
-                                               CheckGlobalResult( );
+                               if (key [0] == '*') {
+                                       if (FastEndsWidth (filename, key.Replace ("*", String.Empty))) {
+                                               global_result = GlobalSufPref [i];
+                                               CheckGlobalResult ();
                                                return true;
                                        }
-                               }
-                               else
-                               {
-                                       if ( filename.StartsWith( key.Replace( "*", "" ) ) )
-                                       {
-                                               global_result = MimeGenerated.GlobalSufPref[ i ];
-                                               CheckGlobalResult( );
+                               } else {
+                                       if (FastStartsWith (filename, key.Replace ("*", String.Empty))) {
+                                               global_result = GlobalSufPref [i];
+                                               CheckGlobalResult ();
                                                return true;
                                        }
                                }
@@ -444,12 +451,10 @@ namespace System.Windows.Forms
                        return false;
                }
                
-               private bool CheckMatchBelow80)
+               private bool CheckMatchBelow80 ()
                {
-                       foreach ( Match match in MimeGenerated.MatchesBelow80 )
-                       {
-                               if ( TestMatch( match ) )
-                               {
+                       foreach (Match match in MatchesBelow80) {
+                               if (TestMatch (match)) {
                                        global_result = match.MimeType;
                                        
                                        return true;
@@ -459,16 +464,14 @@ namespace System.Windows.Forms
                        return false;
                }
                
-               private void CheckForBinaryOrText)
+               private void CheckForBinaryOrText ()
                {
                        // check the first 32 bytes
                        
-                       for ( int i = 0; i < 32; i++ )
-                       {
-                               char c = System.Convert.ToChar( buffer[ i ] );
+                       for (int i = 0; i < 32; i++) {
+                               char c = System.Convert.ToChar (buffer [i]);
                                
-                               if ( c != '\t' &&  c != '\n' && c != '\r' && c != 12 && c < 32 )
-                               {
+                               if (c != '\t' &&  c != '\n' && c != '\r' && c != 12 && c < 32) {
                                        global_result = octet_stream;
                                        return;
                                }
@@ -477,245 +480,562 @@ namespace System.Windows.Forms
                        global_result = text_plain;
                }
                
-               private bool TestMatch( Match match )
+               private bool TestMatch (Match match)
                {
-                       bool found = false;
+                       foreach (Matchlet matchlet in match.Matchlets)
+                               if (TestMatchlet (matchlet))
+                                       return true;
                        
+                       return false;
+               }
+               
+               private bool TestMatchlet (Matchlet matchlet)
+               {
                        //  using a simple brute force search algorithm
-                       // compare each (masked) value from the buffer with the (masked) value from the match
-                       // TODO:
-                       // - to find some more speed, maybe we should use unsafe code
-                       // - check if buffer[0] and buffer[lastmatchbyte] match ByteValue[0] and ByteValue[lastmatchbyte] in a match
-                       
-                       for ( int offset_counter = 0; offset_counter < match.OffsetLength; offset_counter++ )
-                       {
-                               if ( match.Mask == null )
-                               {
-                                       if ( buffer[ match.Offset + offset_counter ] == match.ByteValue[ 0 ] )
-                                       {
-                                               if ( match.ByteValue.Length == 1 )
-                                               {
-                                                       if ( match.Matches.Count > 0 )
-                                                       {
-                                                               foreach ( Match sub_match in match.Matches )
-                                                               {
-                                                                       if ( TestMatch( sub_match ) )
+                       // compare each (masked) value from the buffer with the (masked) value from the matchlet
+                       
+                       // no need to check if the offset + the bytevalue length exceed the # bytes read
+                       if (matchlet.Offset + matchlet.ByteValue.Length > bytes_read)
+                               return false;
+                       
+                       for (int offset_counter = 0; offset_counter < matchlet.OffsetLength; offset_counter++) {
+                               if (matchlet.Offset + offset_counter + matchlet.ByteValue.Length > bytes_read)
+                                       return false;
+                               
+                               if (matchlet.Mask == null) {
+                                       if (buffer [matchlet.Offset + offset_counter] == matchlet.ByteValue [0]) {
+                                               if (matchlet.ByteValue.Length == 1) {
+                                                       if (matchlet.Matchlets.Count > 0) {
+                                                               foreach (Matchlet sub_matchlet in matchlet.Matchlets) {
+                                                                       if (TestMatchlet (sub_matchlet))
                                                                                return true;
                                                                }
-                                                       }
-                                                       else
+                                                       } else
                                                                return true;
                                                }
                                                
-                                               for ( int i = 1; i < match.ByteValue.Length; i++ )
-                                               {
-                                                       if ( buffer[ match.Offset + offset_counter + i ] != match.ByteValue[ i ] )
-                                                       {
-                                                               found = false;
-                                                               break;
-                                                       }
+                                               int minus = 0;
+                                               // check if the last matchlet byte value is the same as the byte value in the buffer...
+                                               if (matchlet.ByteValue.Length > 2) {
+                                                       if (buffer [matchlet.Offset + offset_counter + matchlet.ByteValue.Length - 1] != matchlet.ByteValue [matchlet.ByteValue.Length - 1])
+                                                               return false;
                                                        
-                                                       found = true;
+                                                       minus = 1;
                                                }
                                                
-                                               if ( found )
-                                               {
-                                                       found = false;
-                                                       
-                                                       if ( match.Matches.Count > 0 )
-                                                       {
-                                                               foreach ( Match sub_match in match.Matches )
-                                                               {
-                                                                       if ( TestMatch( sub_match ) )
-                                                                               return true;
-                                                               }
-                                                       }
-                                                       else
-                                                               return true;
+                                               for (int i = 1; i < matchlet.ByteValue.Length - minus; i++) {
+                                                       if (buffer [matchlet.Offset + offset_counter + i] != matchlet.ByteValue [i])
+                                                               return false;
                                                }
+                                               
+                                               if (matchlet.Matchlets.Count > 0) {
+                                                       foreach (Matchlet sub_matchlets in matchlet.Matchlets) {
+                                                               if (TestMatchlet (sub_matchlets))
+                                                                       return true;
+                                                       }
+                                               } else
+                                                       return true;
                                        }
-                               }
-                               else // with mask ( it's the same as above, only AND the byte with the corresponding mask byte
-                               {
-                                       if ( ( buffer[ match.Offset + offset_counter ] & match.Mask[ 0 ] )  ==
-                                           ( match.ByteValue[ 0 ] & match.Mask[ 0 ] ) )
-                                       {
-                                               if ( match.ByteValue.Length == 1 )
-                                               {
-                                                       if ( match.Matches.Count > 0 )
-                                                       {
-                                                               foreach ( Match sub_match in match.Matches )
-                                                               {
-                                                                       if ( TestMatch( sub_match ) )
+                               } else {
+                                       if ((buffer [matchlet.Offset + offset_counter] & matchlet.Mask [0])  ==
+                                           (matchlet.ByteValue [0] & matchlet.Mask [0])) {
+                                               if (matchlet.ByteValue.Length == 1) {
+                                                       if (matchlet.Matchlets.Count > 0) {
+                                                               foreach (Matchlet sub_matchlets in matchlet.Matchlets) {
+                                                                       if (TestMatchlet (sub_matchlets))
                                                                                return true;
                                                                }
-                                                       }
-                                                       else
+                                                       } else
                                                                return true;
                                                }
                                                
-                                               for ( int i = 1; i < match.ByteValue.Length; i++ )
-                                               {
-                                                       if ( ( buffer[ match.Offset + offset_counter + i ]  & match.Mask[ i ] ) !=
-                                                           ( match.ByteValue[ i ] & match.Mask[ i ] ) )
-                                                       {
-                                                               found = false;
-                                                               break;
-                                                       }
+                                               int minus = 0;
+                                               // check if the last matchlet byte value is the same as the byte value in the buffer...
+                                               if (matchlet.ByteValue.Length > 2) {
+                                                       
+                                                       if ((buffer [matchlet.Offset + offset_counter + matchlet.ByteValue.Length - 1] & matchlet.Mask [matchlet.ByteValue.Length - 1])
+                                                           != (matchlet.ByteValue [matchlet.ByteValue.Length - 1] & matchlet.Mask [matchlet.ByteValue.Length - 1]))
+                                                               return false;
                                                        
-                                                       found = true;
+                                                       minus = 1;
                                                }
                                                
-                                               if ( found )
-                                               {
-                                                       found = false;
-                                                       
-                                                       if ( match.Matches.Count > 0 )
-                                                       {
-                                                               foreach ( Match sub_match in match.Matches )
-                                                               {
-                                                                       if ( TestMatch( sub_match ) )
-                                                                               return true;
-                                                               }
-                                                       }
-                                                       else
-                                                               return true;
+                                               for (int i = 1; i < matchlet.ByteValue.Length - minus; i++) {
+                                                       if ((buffer [matchlet.Offset + offset_counter + i]  & matchlet.Mask [i]) !=
+                                                           (matchlet.ByteValue [i] & matchlet.Mask [i]))
+                                                               return false;
                                                }
+                                               
+                                               if (matchlet.Matchlets.Count > 0) {
+                                                       foreach (Matchlet sub_matchlets in matchlet.Matchlets) {
+                                                               if (TestMatchlet (sub_matchlets))
+                                                                       return true;
+                                                       }
+                                               } else
+                                                       return true;
                                        }
                                }
                        }
                        
-                       return found;
+                       return false;
                }
                
-               private bool OpenFile)
+               private bool OpenFile ()
                {
-                       try
-                       {
-                               System.Array.Clear( buffer, 0, buffer.Length );
-                               
-                               file_stream = new FileStream( current_file_name, FileMode.Open, FileAccess.Read ); // FileShare ??? use BinaryReader ???
+                       try {
+                               file_stream = new FileStream (current_file_name, FileMode.Open, FileAccess.Read); // FileShare ??? use BinaryReader ???
                                
-                               if ( file_stream.Length == 0 )
-                               {
+                               if (file_stream.Length == 0) {
                                        global_result = zero_file;
                                        is_zero_file = true;
-                               }
-                               else
-                               {
-                                       file_stream.Read( buffer, 0, buffer.Length );
+                               } else {
+                                       bytes_read = file_stream.Read (buffer, 0, buffer.Length);
+                                       
+                                       // do not clear the whole buffer everytime; clear only what's needed
+                                       if (bytes_read < buffer.Length) {
+                                               System.Array.Clear (buffer, bytes_read, buffer.Length - bytes_read);
+                                       }
                                }
                                
-                               file_stream.Close( );
-                       }
-                       catch (Exception e)
-                       {
+                               file_stream.Close ();
+                       } catch (Exception) {
                                return false;
                        }
                        
                        return true;
                }
                
-               private bool CheckForContentTypeString)
+               private bool CheckForContentTypeString ()
                {
-                       int index = search_string.IndexOf( "Content-type:" );
+                       int index = search_string.IndexOf ("Content-type:");
                        
-                       if ( index != -1 )
-                       {
+                       if (index != -1) {
                                index += 13; // Length of string "Content-type:"
                                
-                               global_result = "";
+                               global_result = String.Empty;
                                
-                               while ( search_string[ index ] != ';' )
-                               {
-                                       global_result += search_string[ index++ ];
+                               while (search_string [index] != ';') {
+                                       global_result += search_string [index++];
                                }
                                
-                               global_result.Trim);
+                               global_result.Trim ();
                                
                                return true;
                        }
                        
                        // convert string to byte array
-                       byte[] string_byte = ( new ASCIIEncoding( ) ).GetBytes( search_string );
+                       byte[] string_byte = (new ASCIIEncoding ()).GetBytes (search_string);
                        
-                       System.Array.Clear( buffer, 0, buffer.Length );
+                       System.Array.Clear (buffer, 0, buffer.Length);
                        
-                       if ( string_byte.Length > buffer.Length )
-                       {
-                               System.Array.Copy( string_byte, buffer, buffer.Length );
-                       }
-                       else
-                       {
-                               System.Array.Copy( string_byte, buffer, string_byte.Length );
+                       if (string_byte.Length > buffer.Length) {
+                               System.Array.Copy (string_byte, buffer, buffer.Length);
+                       } else {
+                               System.Array.Copy (string_byte, buffer, string_byte.Length);
                        }
                        
-                       if ( CheckMatch80Plus( ) )
+                       if (CheckMatch80Plus ())
                                return true;
                        
-                       if ( CheckMatchBelow80( ) )
+                       if (CheckMatchBelow80 ())
                                return true;
                        
                        return false;
                }
        }
        
-       internal class MimeType
+       internal class FDOMimeConfigReader
        {
-               private string comment;
-               private Hashtable commentsLanguage = new Hashtable();
+               bool fdo_mime_available = false;
+               StringCollection shared_mime_paths = new StringCollection ();
+               BinaryReader br;
                
-               public string Comment
+               int max_offset_and_range = 0;
+               
+               public int Init ()
                {
-                       get {
-                               return comment;
+                       CheckFDOMimePaths ();
+                       
+                       if (!fdo_mime_available)
+                               return -1;
+                       
+                       ReadMagicData ();
+                       
+                       ReadGlobsData ();
+                       
+                       ReadSubclasses ();
+                       
+                       ReadAliases ();
+                       
+                       shared_mime_paths = null;
+                       br = null;
+                       
+                       return max_offset_and_range;
+               }
+               
+               private void CheckFDOMimePaths ()
+               {
+                       if (Directory.Exists ("/usr/share/mime"))
+                               shared_mime_paths.Add ("/usr/share/mime/");
+                       else
+                       if (Directory.Exists ("/usr/local/share/mime"))
+                               shared_mime_paths.Add ("/usr/local/share/mime/");
+                       
+                       if (Directory.Exists (System.Environment.GetFolderPath (Environment.SpecialFolder.Personal) + "/.local/share/mime"))
+                               shared_mime_paths.Add (System.Environment.GetFolderPath (Environment.SpecialFolder.Personal) + "/.local/share/mime/");
+                       
+                       if (shared_mime_paths.Count == 0)
+                               return;
+                       
+                       fdo_mime_available = true;
+               }
+               
+               private void ReadMagicData ()
+               {
+                       foreach (string path in shared_mime_paths) {
+                               if (!File.Exists (path + "/magic"))
+                                       continue;
+                               
+                               try {
+                                       FileStream fs = File.OpenRead (path + "/magic");
+                                       br = new BinaryReader (fs);
+                                       
+                                       if (CheckMagicHeader ()) {
+                                               MakeMatches ();
+                                       }
+                                       
+                                       br.Close ();
+                                       fs.Close ();
+                               } catch (Exception ) {
+                               }
                        }
-                       set {
-                               comment = value;
+               }
+               
+               private void MakeMatches ()
+               {
+                       Matchlet[] matchlets = new Matchlet [30];
+                       
+                       while (br.PeekChar () != -1) {
+                               int priority = -1;
+                               string mime_type = ReadPriorityAndMimeType (ref priority);
+                               
+                               if (mime_type != null) {
+                                       Match match = new Match ();
+                                       match.Priority = priority;
+                                       match.MimeType = mime_type;
+                                       
+                                       while (true) {
+                                               int indent = 0;
+                                               // indent
+                                               char c;
+                                               if (br.PeekChar () != '>') {
+                                                       StringBuilder indent_string = new StringBuilder ();
+                                                       //string indent_string = String.Empty;
+                                                       while (true) {
+                                                               if (br.PeekChar () == '>')
+                                                                       break;
+                                                               
+                                                               c = br.ReadChar ();
+                                                               //indent_string += c;
+                                                               indent_string.Append (c);
+                                                       }
+                                                       indent = Convert.ToInt32 (indent_string.ToString ());
+                                               }
+                                               
+                                               int offset = 0;
+                                               
+                                               // offset
+                                               if (br.PeekChar () == '>') {
+                                                       br.ReadChar ();
+                                                       offset = ReadValue ();
+                                               }
+                                               
+                                               int value_length = 0;
+                                               byte[] value = null;
+                                               // value length and value
+                                               if (br.PeekChar () == '=') {
+                                                       br.ReadChar ();
+                                                       
+                                                       // read 2 bytes value length (always big endian)
+                                                       byte first = br.ReadByte ();
+                                                       byte second = br.ReadByte ();
+                                                       
+                                                       value_length = first * 256 + second;
+                                                       
+                                                       value = br.ReadBytes (value_length);
+                                               }
+                                               
+                                               // mask
+                                               byte[] mask = null;
+                                               
+                                               if (br.PeekChar () == '&') {
+                                                       br.ReadChar ();
+                                                       
+                                                       mask = br.ReadBytes (value_length);
+                                               }
+                                               
+                                               // word_size
+                                               int word_size = 1;
+                                               if (br.PeekChar () == '~') {
+                                                       br.ReadChar ();
+                                                       
+                                                       c = br.ReadChar ();
+                                                       
+                                                       word_size = Convert.ToInt32 (c - 0x30);
+                                                       
+                                                       // data is stored in big endian format. 
+                                                       if (word_size > 1 && System.BitConverter.IsLittleEndian) {
+                                                               //convert the value and, if available, the mask data to little endian
+                                                               if (word_size == 2) {
+                                                                       if (value != null) {
+                                                                               for (int i = 0; i < value.Length; i += 2) {
+                                                                                       byte one = value [i];
+                                                                                       byte two = value [i + 1];
+                                                                                       value [i] = two;
+                                                                                       value [i + 1] = one;
+                                                                               }
+                                                                       }
+                                                                       if (mask != null) {
+                                                                               for (int i = 0; i < mask.Length; i += 2) {
+                                                                                       byte one = mask [i];
+                                                                                       byte two = mask [i + 1];
+                                                                                       mask [i] = two;
+                                                                                       mask [i + 1] = one;
+                                                                               }
+                                                                       }
+                                                               } else if (word_size == 4) {
+                                                                       if (value != null) {
+                                                                               for (int i = 0; i < value.Length; i += 4) {
+                                                                                       byte one = value [i];
+                                                                                       byte two = value [i + 1];
+                                                                                       byte three = value [i + 2];
+                                                                                       byte four = value [i + 3];
+                                                                                       value [i] = four;
+                                                                                       value [i + 1] = three;
+                                                                                       value [i + 2] = two;
+                                                                                       value [i + 3] = one;
+                                                                               }
+                                                                       }
+                                                                       if (mask != null) {
+                                                                               for (int i = 0; i < mask.Length; i += 4) {
+                                                                                       byte one = mask [i];
+                                                                                       byte two = mask [i + 1];
+                                                                                       byte three = mask [i + 2];
+                                                                                       byte four = mask [i + 3];
+                                                                                       mask [i] = four;
+                                                                                       mask [i + 1] = three;
+                                                                                       mask [i + 2] = two;
+                                                                                       mask [i + 3] = one;
+                                                                                       
+                                                                               }
+                                                                       }
+                                                               }
+                                                       }
+                                               }
+                                               
+                                               // range length
+                                               int range_length = 1;
+                                               if (br.PeekChar () == '+') {
+                                                       br.ReadChar ();
+                                                       range_length = ReadValue ();
+                                               }
+                                               
+                                               // read \n
+                                               br.ReadChar ();
+                                               
+                                               // create the matchlet
+                                               matchlets [indent] = new Matchlet ();
+                                               matchlets [indent].Offset = offset;
+                                               matchlets [indent].OffsetLength = range_length;
+                                               matchlets [indent].ByteValue = value;
+                                               if (mask != null)
+                                                       matchlets [indent].Mask = mask;
+                                               
+                                               if (indent == 0) {
+                                                       match.Matchlets.Add (matchlets [indent]);
+                                               } else {
+                                                       matchlets [indent - 1].Matchlets.Add (matchlets [indent]);
+                                               }
+                                               
+                                               if (max_offset_and_range < matchlets [indent].Offset + matchlets [indent].OffsetLength + matchlets [indent].ByteValue.Length + 1)
+                                                       max_offset_and_range = matchlets [indent].Offset + matchlets [indent].OffsetLength + matchlets [indent].ByteValue.Length  + 1;
+                                               
+                                               // if '[' move to next mime type
+                                               if (br.PeekChar () == '[')
+                                                       break;
+                                       }
+                                       
+                                       if (priority < 80)
+                                               Mime.MatchesBelow80.Add (match);
+                                       else
+                                               Mime.Matches80Plus.Add (match);
+                               }
                        }
                }
                
-               public Hashtable CommentsLanguage
+               private void ReadGlobsData ()
                {
-                       get {
-                               return commentsLanguage;
+                       foreach (string path in shared_mime_paths) {
+                               if (!File.Exists (path + "/globs"))
+                                       continue;
+                               
+                               try {
+                                       StreamReader sr = new StreamReader (path + "/globs");
+                                       
+                                       while (sr.Peek () != -1) {
+                                               string line = sr.ReadLine ().Trim ();
+                                               
+                                               if (line.StartsWith ("#"))
+                                                       continue;
+                                               
+                                               string[] split = line.Split (new char [] {':'});
+                                               
+                                               if (split [1].IndexOf ('*') > -1 && split [1].IndexOf ('.') == -1) {
+                                                       Mime.GlobalSufPref.Add (split [1], split [0]);
+                                               } else if (split [1]. IndexOf ('*') == -1) {
+                                                       Mime.GlobalLiterals.Add (split [1], split [0]);
+                                               } else {
+                                                       string[] split2 = split [1].Split (new char [] {'.'});
+                                                       
+                                                       if (split2.Length > 2) {
+                                                               // more than one dot
+                                                               Mime.GlobalPatternsLong.Add (split [1].Remove (0, 1), split [0]);
+                                                       } else {
+                                                               // normal
+                                                               Mime.GlobalPatternsShort.Add (split [1].Remove (0, 1), split [0]);
+                                                       }
+                                               }
+                                       }
+                                       
+                                       sr.Close ();
+                               } catch (Exception ) {
+                               }
                        }
-                       set {
-                               commentsLanguage = value;
+               }
+               
+               private void ReadSubclasses ()
+               {
+                       foreach (string path in shared_mime_paths) {
+                               if (!File.Exists (path + "/subclasses"))
+                                       continue;
+                               
+                               try {
+                                       StreamReader sr = new StreamReader (path + "/subclasses");
+                                       
+                                       while (sr.Peek () != -1) {
+                                               string line = sr.ReadLine ().Trim ();
+                                               
+                                               if (line.StartsWith ("#"))
+                                                       continue;
+                                               
+                                               string[] split = line.Split (new char [] {' '});
+                                               
+                                               Mime.SubClasses.Add (split [0], split [1]);
+                                       }
+                                       
+                                       sr.Close ();
+                               } catch (Exception ) {
+                               }
                        }
                }
-               public string GetCommentForLanguage( string language )
+               
+               private void ReadAliases ()
                {
-                       return commentsLanguage[ language ] as string;
+                       foreach (string path in shared_mime_paths) {
+                               if (!File.Exists (path + "/aliases"))
+                                       continue;
+                               
+                               try {
+                                       StreamReader sr = new StreamReader (path + "/aliases");
+                                       
+                                       while (sr.Peek () != -1) {
+                                               string line = sr.ReadLine ().Trim ();
+                                               
+                                               if (line.StartsWith ("#"))
+                                                       continue;
+                                               
+                                               string[] split = line.Split (new char [] {' '});
+                                               
+                                               Mime.Aliases.Add (split [0], split [1]);
+                                       }
+                                       
+                                       sr.Close ();
+                               } catch (Exception ) {
+                               }
+                       }
+               }
+               
+               private int ReadValue ()
+               {
+                       StringBuilder result_string = new StringBuilder ();
+                       int result = 0;
+                       char c;
+                       
+                       while (true) {
+                               if (br.PeekChar () == '=' || br.PeekChar () == '\n')
+                                       break;
+                               
+                               c = br.ReadChar ();
+                               result_string.Append (c);
+                       }
+                       
+                       result = Convert.ToInt32 (result_string.ToString ());
+                       
+                       return result;
+               }
+               
+               private string ReadPriorityAndMimeType (ref int priority)
+               {
+                       if (br.ReadChar () == '[') {
+                               StringBuilder priority_string = new StringBuilder ();
+                               while (true) {
+                                       char c = br.ReadChar ();
+                                       if (c == ':')
+                                               break;
+                                       priority_string.Append (c);
+                               }
+                               
+                               priority = System.Convert.ToInt32 (priority_string.ToString ());
+                               
+                               StringBuilder mime_type_result = new StringBuilder ();
+                               while (true) {
+                                       char c = br.ReadChar ();
+                                       if (c == ']')
+                                               break;
+                                       
+                                       mime_type_result.Append (c);
+                               }
+                               
+                               if (br.ReadChar () == '\n')
+                                       return mime_type_result.ToString ();
+                       }
+                       return null;
+               }
+               
+               private bool CheckMagicHeader ()
+               {
+                       char[] chars = br.ReadChars (10);
+                       string magic_header = new String (chars);
+                       
+                       if (magic_header != "MIME-Magic")
+                               return false;
+                       
+                       if (br.ReadByte () != 0)
+                               return false;
+                       if (br.ReadChar () != '\n')
+                               return false;
+                       
+                       return true;
                }
-       }
-       
-       internal enum MatchTypes
-       {
-               TypeString,
-               TypeHost16,
-               TypeHost32,
-               TypeBig16,
-               TypeBig32,
-               TypeLittle16,
-               TypeLittle32,
-               TypeByte
        }
        
        internal class Match
        {
                string mimeType;
-               byte[] byteValue;
-               byte[] mask = null;
                int priority;
-               int offset;
-               int offsetLength;
-               int wordSize = 1;
-               MatchTypes matchType;
-               ArrayList matches = new ArrayList();
+               ArrayList matchlets = new ArrayList();
                
-               public string MimeType
-               {
+               public string MimeType {
                        set {
                                mimeType = value;
                        }
@@ -725,52 +1045,55 @@ namespace System.Windows.Forms
                        }
                }
                
-               public byte[] ByteValue
-               {
+               public int Priority {
                        set {
-                               byteValue = value;
+                               priority = value;
                        }
                        
                        get {
-                               return byteValue;
+                               return priority;
                        }
                }
                
-               public byte[] Mask
-               {
-                       set {
-                               mask = value;
-                       }
-                       
+               public ArrayList Matchlets {
                        get {
-                               return mask;
+                               return matchlets;
                        }
                }
+       }
+       
+       internal class Matchlet
+       {
+               byte[] byteValue;
+               byte[] mask = null;
                
-               public int Priority
-               {
+               int offset;
+               int offsetLength;
+               int wordSize = 1;
+               
+               ArrayList matchlets = new ArrayList ();
+               
+               public byte[] ByteValue {
                        set {
-                               priority = value;
+                               byteValue = value;
                        }
                        
                        get {
-                               return priority;
+                               return byteValue;
                        }
                }
                
-               public ArrayList Matches
-               {
+               public byte[] Mask {
                        set {
-                               matches = value;
+                               mask = value;
                        }
                        
                        get {
-                               return matches;
+                               return mask;
                        }
                }
                
-               public int Offset
-               {
+               public int Offset {
                        set {
                                offset = value;
                        }
@@ -780,8 +1103,7 @@ namespace System.Windows.Forms
                        }
                }
                
-               public int OffsetLength
-               {
+               public int OffsetLength {
                        set {
                                offsetLength = value;
                        }
@@ -791,8 +1113,7 @@ namespace System.Windows.Forms
                        }
                }
                
-               public int WordSize
-               {
+               public int WordSize {
                        set {
                                wordSize = value;
                        }
@@ -802,14 +1123,9 @@ namespace System.Windows.Forms
                        }
                }
                
-               public MatchTypes MatchType
-               {
-                       set {
-                               matchType = value;
-                       }
-                       
+               public ArrayList Matchlets {
                        get {
-                               return matchType;
+                               return matchlets;
                        }
                }
        }