[Arakhnę-Dev] [265] * Bug fix: recursive deletion of directories.

[ Thread Index | Date Index | More arakhne.org/dev Archives ]


Revision: 265
Author:   galland
Date:     2011-08-21 23:16:53 +0200 (Sun, 21 Aug 2011)
Log Message:
-----------
* Bug fix: recursive deletion of directories.

Modified Paths:
--------------
    trunk/arakhneVmutils/java/src/main/java/org/arakhne/vmutil/FileSystem.java

Modified: trunk/arakhneVmutils/java/src/main/java/org/arakhne/vmutil/FileSystem.java
===================================================================
--- trunk/arakhneVmutils/java/src/main/java/org/arakhne/vmutil/FileSystem.java	2011-08-21 14:45:08 UTC (rev 264)
+++ trunk/arakhneVmutils/java/src/main/java/org/arakhne/vmutil/FileSystem.java	2011-08-21 21:16:53 UTC (rev 265)
@@ -41,6 +41,7 @@
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Random;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 import java.util.zip.ZipEntry;
@@ -59,10 +60,10 @@
 
 	static {
 		URLHandlerUtil.installArakhneHandlers();
-		
+
 		String validChars = "[^\\\\/:*?\"<>|]"; //$NON-NLS-1$
 		String bslashChar = "\\\\"; //$NON-NLS-1$
-		
+
 		StringBuffer pattern = new StringBuffer();
 		pattern.append("^"); //$NON-NLS-1$
 		pattern.append("(([a-zA-Z]:"); //$NON-NLS-1$
@@ -82,15 +83,15 @@
 		//"^([A-Za-z]:)?([^\\\\/:*?\"<>|]*\\\\)*[^\\\\/:*?\"<>|]*$"; //$NON-NLS-1$
 		WINDOW_NATIVE_FILENAME_PATTERN = pattern.toString();
 	}
-	
+
 	/** Regular expression pattern which corresponds to Windows native filename.
 	 */
 	private static final String WINDOW_NATIVE_FILENAME_PATTERN;
-	
+
 	/** Character used to specify a file extension.
 	 */
 	public static final char EXTENSION_SEPARATOR_CHAR = '.';
-	
+
 	/** String which is representing the current directory in a relative path.
 	 */
 	public static final String CURRENT_DIRECTORY = "."; //$NON-NLS-1$
@@ -110,13 +111,15 @@
 	/** String used to specify a file extension.
 	 */
 	public static final String EXTENSION_SEPARATOR = "."; //$NON-NLS-1$
-	
+
 	/** Prefix used to join in a Jar URL the jar filename and the inside-jar filename.
 	 */
 	public static final String JAR_URL_FILE_ROOT = "!/"; //$NON-NLS-1$
+
+	private static final Random RANDOM = new Random();
 	
 	private static final DeleteOnExitHook deleteOnExitHook = new DeleteOnExitHook();
-	
+
 	/** Decode the given string.
 	 * 
 	 * @param s
@@ -136,7 +139,7 @@
 	public static boolean isJarURL(URL url) {
 		return URISchemeType.JAR.isURL(url);
 	}
-	
+
 	/** Replies the jar part of the jar-scheme URL.
 	 * 
 	 * @param url
@@ -238,7 +241,7 @@
 		}
 		return new URL(URISchemeType.JAR.name(), "", buf.toString()); //$NON-NLS-1$
 	}
-	
+
 	/** Replies if the current operating system uses case-sensitive filename.
 	 * 
 	 * @return <code>true</code> if the filenames on the current file system are case sensitive,
@@ -262,7 +265,7 @@
 			return false;
 		}
 	}
-	
+
 	/** Replies the character used to separate the basename and the file extension.
 	 * 
 	 * @return the character used to separate the basename and the file extension.
@@ -306,7 +309,7 @@
 	 */
 	public static URL dirname(URL filename) {
 		if (filename==null) return null;
-		
+
 		URL prefix = null;
 		String path;
 		if (isJarURL(filename)) {
@@ -315,9 +318,9 @@
 		}
 		else
 			path = filename.getPath();
-		
+
 		if ("".equals(path)) return null; //$NON-NLS-1$
-		
+
 		int idx = path.lastIndexOf(URL_PATH_SEPARATOR_CHAR);
 		if (idx==path.length()-1)
 			idx = path.lastIndexOf(URL_PATH_SEPARATOR_CHAR, path.length()-2);
@@ -331,7 +334,7 @@
 		else {
 			path = path.substring(0, idx+1);
 		}
-				
+
 		try {
 			if (prefix!=null) {
 				return toJarURL(prefix, path);
@@ -362,7 +365,7 @@
 		}
 		return null;
 	}
-	
+
 	/** Replies the basename of the specified file with the extension.
 	 * <p>
 	 * Caution: This function does not support URL format.
@@ -532,7 +535,7 @@
 		}
 		else
 			basename = filename.substring(idx+1, end+1);
-		
+
 		idx = basename.indexOf(getFileExtensionCharacter());
 		if (idx<0) return basename;
 		return basename.substring(0,idx);
@@ -576,7 +579,7 @@
 		}
 		else
 			basename = largeBasename.substring(idx+1, end+1);
-		
+
 		idx = basename.indexOf(getFileExtensionCharacter());
 		if (idx<0) return basename;
 		return basename.substring(0,idx);
@@ -688,8 +691,8 @@
 			if (!empty) {
 				assert(elt!=null);
 				if (!elt.startsWith(File.separator) 
-					&& buf.length()>=0
-					&& buf.charAt(buf.length()-1)!=File.separatorChar) {
+						&& buf.length()>=0
+						&& buf.charAt(buf.length()-1)!=File.separatorChar) {
 					buf.append(File.separatorChar);
 				}
 				buf.append(elt);
@@ -733,8 +736,8 @@
 			if (!empty) {
 				assert(elt!=null);
 				if (!elt.startsWith(File.separator) 
-					&& (buf.length()==0
-						|| buf.charAt(buf.length()-1)!=URL_PATH_SEPARATOR_CHAR)) {
+						&& (buf.length()==0
+								|| buf.charAt(buf.length()-1)!=URL_PATH_SEPARATOR_CHAR)) {
 					buf.append(URL_PATH_SEPARATOR_CHAR);
 				}
 				buf.append(elt);
@@ -1064,7 +1067,7 @@
 			candidates.add(file);
 			File f;
 			File[] children;
-			while (candidates.isEmpty()) {
+			while (!candidates.isEmpty()) {
 				f = candidates.getFirst();
 				if (f.isDirectory()) {
 					children = f.listFiles();
@@ -1088,7 +1091,7 @@
 			}
 		}
 	}
-	
+
 	/** Delete the given directory and all its subdirectories when the JVM is exiting.
 	 * If the given <var>file</var> is a directory, its
 	 * content and the <var>file</var> itself are recursivelly removed.
@@ -1178,7 +1181,7 @@
 			if (outChannel!=null) outChannel.close();
 		}
 	}
-	
+
 	/** Copy the first file into the second file.
 	 * <p>
 	 * The content of the second file will be lost.
@@ -1299,7 +1302,7 @@
 		}
 		return null;
 	}
-	
+
 	/** Replies the user configuration directory for the specified software.
 	 * <p>
 	 * On Unix operating systems, the user directory for a
@@ -1350,7 +1353,7 @@
 		}
 		return null;
 	}
-	
+
 	/** Replies the user configuration directory for the specified software.
 	 * <p>
 	 * On Unix operating systems, the system directory for a
@@ -1430,7 +1433,7 @@
 	public static File convertUrlToFile(URL url) {
 		return convertURLToFile(url);
 	}
-	
+
 	/** Convert an URL which represents a local file into a File.
 	 * 
 	 * @param url is the URL to convert.
@@ -1489,7 +1492,7 @@
 		}
 		throw new IllegalArgumentException("not a file URL: "+url); //$NON-NLS-1$
 	}
-	
+
 	/** Convert a string to an URL according to several rules.
 	 * <p>
 	 * The rules are (the first succeeded is replied):
@@ -1579,7 +1582,7 @@
 	public static URL convertStringToUrl(String urlDescription, boolean allowResourceSearch, boolean repliesFileURL) {
 		return convertStringToURL(urlDescription, allowResourceSearch, repliesFileURL, true);
 	}
-	
+
 	/** Convert a string to an URL according to several rules.
 	 * <p>
 	 * The rules are (the first succeeded is replied):
@@ -1670,16 +1673,16 @@
 					// ignore error
 				}
 			}
-			
+
 			if (url==null) {
 				if (allowResourceSearch) {
 					url = Resources.getResource(urlDescription);
 				}
-				
+
 				if (url==null && URISchemeType.RESOURCE.isScheme(urlDescription)) {
 					return null;
 				}
-		
+
 				if (url==null && repliesFileURL) {
 					String urlPart = URISchemeType.removeAnyScheme(urlDescription);
 					// Try to parse a malformed JAR url:
@@ -1698,7 +1701,7 @@
 							}
 						}
 					}
-					
+
 					// Standard local file
 					if (url==null) {
 						try {
@@ -1712,7 +1715,7 @@
 				}
 			}
 		}
-		
+
 		return url;
 	}
 
@@ -1773,7 +1776,7 @@
 		}
 		return filename;
 	}
-	
+
 	/** Replies if the given URL is using a protocol which could be map to files.
 	 * 
 	 * @param url
@@ -1788,7 +1791,7 @@
 		}
 		return false;
 	}
-	
+
 	/** Replies if the given URL scheme is using a protocol which could be map to files.
 	 * 
 	 * @param scheme
@@ -2004,15 +2007,15 @@
 			}
 			break;
 		case FILE:
-			{
-				File f = new File(filename.getFile());
-				if (!f.isAbsolute()) {
-					if (current!=null) {
-						return join(current, f);
-					}
+		{
+			File f = new File(filename.getFile());
+			if (!f.isAbsolute()) {
+				if (current!=null) {
+					return join(current, f);
 				}
 			}
-			break;
+		}
+		break;
 		default:
 			// do not change the URL
 		}
@@ -2111,27 +2114,27 @@
 
 		switch(URISchemeType.getSchemeType(url)) {
 		case JAR:
-			{
-				int index = path.indexOf(JAR_URL_FILE_ROOT);
-				assert(index>0);
-				prefix = path.substring(0,index+1);
-				path = path.substring(index+1);
-				parentStr = URL_PATH_SEPARATOR;
-			}
-			break;
+		{
+			int index = path.indexOf(JAR_URL_FILE_ROOT);
+			assert(index>0);
+			prefix = path.substring(0,index+1);
+			path = path.substring(index+1);
+			parentStr = URL_PATH_SEPARATOR;
+		}
+		break;
 		case FILE:
-			{
-				prefix = null;
-				parentStr = ".."+URL_PATH_SEPARATOR; //$NON-NLS-1$
-			}
-			break;
+		{
+			prefix = null;
+			parentStr = ".."+URL_PATH_SEPARATOR; //$NON-NLS-1$
+		}
+		break;
 		default:
-			{
-				prefix = null;
-				parentStr = URL_PATH_SEPARATOR;
-			}
+		{
+			prefix = null;
+			parentStr = URL_PATH_SEPARATOR;
 		}
-		
+		}
+
 		if (path==null || "".equals(path)) path = parentStr; //$NON-NLS-1$
 		int index = path.lastIndexOf(URL_PATH_SEPARATOR_CHAR);
 		if (index==-1) path = parentStr;
@@ -2141,12 +2144,12 @@
 			else path = path.substring(0, index+1);
 		}
 		else path = path.substring(0, index+1);
-		
+
 		if (prefix!=null)  path = prefix + path;
-		
+
 		return new URL(url.getProtocol(), url.getHost(), url.getPort(), path);
 	}
-	
+
 	/** Test if the given filename is a local filename and extract
 	 * the path component.
 	 * 
@@ -2164,7 +2167,7 @@
 			fn = filename;
 		return fn;
 	}
-	
+
 	/** Replies if the given string contains a Windows&reg; native long filename.
 	 * <p>
 	 * Long filenames (LFN), spelled "long file names" by Microsoft Corporation, 
@@ -2195,7 +2198,7 @@
 		Matcher matcher = pattern.matcher(fn);
 		return matcher.matches();
 	}
-	
+
 	/** Normalize the given string contains a Windows&reg; native long filename
 	 * and replies a Java-standard version.
 	 * <p>
@@ -2230,7 +2233,7 @@
 		}
 		return null;
 	}
-	
+
 	/** Replies an URL for the given file and translate it into a
 	 * resource URL if the given file is inside the classpath.
 	 * 
@@ -2253,7 +2256,7 @@
 			return null;
 		}
 	}
-	
+
 	/** Replies an URL for the given url and translate it into a
 	 * resource URL if the given file is inside the classpath.
 	 * 
@@ -2268,7 +2271,7 @@
 		String sp;
 		Iterator<URL> classpath = ClasspathUtil.getClasspath();
 		URL path;
-		
+
 		while (classpath.hasNext()) {
 			path = classpath.next();
 			sp = path.toExternalForm().replaceAll("/$", "");  //$NON-NLS-1$//$NON-NLS-2$
@@ -2283,7 +2286,7 @@
 				}
 			}
 		}
-		
+
 		return url;
 	}
 
@@ -2298,21 +2301,21 @@
 	public static File makeRelative(File filenameToMakeRelative, File rootPath) throws IOException {
 		if (filenameToMakeRelative==null || rootPath==null)
 			throw new IllegalArgumentException();
-		
+
 		if (!filenameToMakeRelative.isAbsolute()) return filenameToMakeRelative;
 		if (!rootPath.isAbsolute()) return filenameToMakeRelative;
 
 		File root = rootPath.getCanonicalFile();
 		File dir = filenameToMakeRelative.getParentFile().getCanonicalFile();
-		
+
 		String[] parts1 = split(dir);
 		String[] parts2 = split(root);
-		
+
 		String relPath = makeRelative(parts1, parts2, filenameToMakeRelative.getName());
-		
+
 		return new File(CURRENT_DIRECTORY, relPath);
 	}
-	
+
 	/**
 	 * Make the given filename relative to the given root path.
 	 *
@@ -2325,16 +2328,16 @@
 	public static File makeRelative(File filenameToMakeRelative, URL rootPath) throws IOException {
 		if (filenameToMakeRelative==null || rootPath==null)
 			throw new IllegalArgumentException();
-		
+
 		if (!filenameToMakeRelative.isAbsolute()) return filenameToMakeRelative;
 
 		File dir = filenameToMakeRelative.getParentFile().getCanonicalFile();
-		
+
 		String[] parts1 = split(dir);
 		String[] parts2 = split(rootPath);
-		
+
 		String relPath = makeRelative(parts1, parts2, filenameToMakeRelative.getName());
-		
+
 		return new File(CURRENT_DIRECTORY, relPath);
 	}
 
@@ -2350,27 +2353,27 @@
 	public static File makeRelative(URL filenameToMakeRelative, URL rootPath) throws IOException {
 		if (filenameToMakeRelative==null || rootPath==null)
 			throw new IllegalArgumentException();
-		
+
 		String basename = largeBasename(filenameToMakeRelative);
 		URL dir = dirname(filenameToMakeRelative);
-		
+
 		String[] parts1 = split(dir);
 		String[] parts2 = split(rootPath);
-		
+
 		String relPath = makeRelative(parts1, parts2, basename);
-		
+
 		return new File(CURRENT_DIRECTORY, relPath);
 	}
 
 	private static String makeRelative(String[] parts1, String[] parts2, String basename) {
 		int firstDiff = -1;
-		
+
 		for(int i=0; firstDiff<0 && i<parts1.length && i<parts2.length; i++) {
 			if (!parts1[i].equals(parts2[i])) {
 				firstDiff = i;
 			}
 		}
-		
+
 		StringBuffer result = new StringBuffer();
 		if (firstDiff<0) {
 			firstDiff = Math.min(parts1.length, parts2.length);
@@ -2388,15 +2391,15 @@
 
 		if (result.length()>0) result.append(File.separator);
 		result.append(basename);
-		
+
 		return result.toString();
 	}
-	
+
 	/**
-     * <p>
-     * A canonical pathname is both absolute and unique.  This method maps 
-     * the pathname to its unique form.  This typically involves removing redundant names
-     * such as <tt>"."</tt> and <tt>".."</tt> from the pathname.
+	 * <p>
+	 * A canonical pathname is both absolute and unique.  This method maps 
+	 * the pathname to its unique form.  This typically involves removing redundant names
+	 * such as <tt>"."</tt> and <tt>".."</tt> from the pathname.
 	 * 
 	 * @param url is the URL to make canonical
 	 * @return the canonical form of the given URL.
@@ -2405,7 +2408,7 @@
 	public static URL makeCanonicalURL(URL url) {
 		if (url!=null) {
 			String[] pathComponents = url.getPath().split(Pattern.quote(URL_PATH_SEPARATOR));
-			
+
 			List<String> canonicalPath = new LinkedList<String>();
 			for(String component : pathComponents) {
 				if (!CURRENT_DIRECTORY.equals(component)) {
@@ -2422,7 +2425,7 @@
 					}
 				}
 			}
-			
+
 			StringBuffer newPathBuffer = new StringBuffer();
 			boolean isFirst = true;
 			for(String component : canonicalPath) {
@@ -2434,7 +2437,7 @@
 				}
 				newPathBuffer.append(component);
 			}
-			
+
 			try {
 				URI uri = new URI(
 						url.getProtocol(), 
@@ -2480,18 +2483,18 @@
 		ZipOutputStream zos = null;
 		try {
 			zos = new ZipOutputStream(output);
-			
+
 			if (input==null) return;
 
 			LinkedList<File> candidates = new LinkedList<File>();
 			candidates.add(input);
-			
+
 			byte[] buffer = new byte[2048];
 			int len;
 			File file;
-			
+
 			File rootDirectory = (input.isDirectory()) ? input : input.getParentFile();
-			
+
 			while (!candidates.isEmpty()) {
 				file = candidates.removeFirst();
 				assert(file!=null);
@@ -2521,7 +2524,7 @@
 			if (zos!=null) zos.close();
 		}
 	}
-	
+
 	/**
 	 * Unzip the given stream and write out the file in the output.
 	 * If the input file is a directory, the content of the directory is zipped.
@@ -2539,7 +2542,7 @@
 		ZipInputStream zis = null;
 		try {
 			zis = new ZipInputStream(input);
-			
+
 		}
 		finally {
 			if (zis!=null) zis.close();
@@ -2570,7 +2573,126 @@
 		unzipFile(new FileInputStream(input), output);
 	}
 
-	/** Hook to recursively delete files on JVM exit. 
+	/** Create an empty directory in the default temporary-file directory, using
+	 * the given prefix and suffix to generate its name.  Invoking this method
+	 * is equivalent to invoking <code>{@link #createTempDirectory(java.lang.String,
+	 * java.lang.String, java.io.File)
+	 * createTempDirectory(prefix,&nbsp;suffix,&nbsp;null)}</code>.
+	 *
+	 * @param  prefix is the prefix string to be used in generating the file's
+	 *                    name; must be at least three characters long
+	 *
+	 * @param  suffix is the suffix string to be used in generating the file's
+	 *                    name; may be <code>null</code>, in which case the
+	 *                    suffix <code>".tmp"</code> will be used
+	 * @return  An abstract pathname denoting a newly-created empty file
+	 * @throws  IllegalArgumentException
+	 *          If the <code>prefix</code> argument contains fewer than three
+	 *          characters
+	 * @throws  IOException  If a file could not be created
+	 * @throws  SecurityException
+	 *          If a security manager exists and its <code>{@link
+	 *          java.lang.SecurityManager#checkWrite(java.lang.String)}</code>
+	 *          method does not allow a file to be created
+	 * @since 6.2
+	 */
+	public static File createTempDirectory(String prefix, String suffix) throws IOException {
+		return createTempDirectory(prefix, suffix, null);
+	}
+
+	/** Creates a new empty directory in the specified directory, using the
+     * given prefix and suffix strings to generate its name.  If this method
+     * returns successfully then it is guaranteed that:
+     * <ol>
+     * <li> The directory denoted by the returned abstract pathname did not exist
+     *      before this method was invoked, and
+     * <li> Neither this method nor any of its variants will return the same
+     *      abstract pathname again in the current invocation of the virtual
+     *      machine.
+     * </ol>
+     * <p>
+     * This method provides only part of a temporary-file facility.  To arrange
+     * for a file created by this method to be deleted automatically, use the
+     * <code>{@link #deleteOnExit}</code> method.
+     *
+     * <p> The <code>prefix</code> argument must be at least three characters
+     * long.  It is recommended that the prefix be a short, meaningful string
+     * such as <code>"hjb"</code> or <code>"mail"</code>.  The
+     * <code>suffix</code> argument may be <code>null</code>, in which case the
+     * suffix <code>".tmp"</code> will be used.
+     *
+     * <p> To create the new directory, the prefix and the suffix may first be
+     * adjusted to fit the limitations of the underlying platform.  If the
+     * prefix is too long then it will be truncated, but its first three
+     * characters will always be preserved.  If the suffix is too long then it
+     * too will be truncated, but if it begins with a period character
+     * (<code>'.'</code>) then the period and the first three characters
+     * following it will always be preserved.  Once these adjustments have been
+     * made the name of the new file will be generated by concatenating the
+     * prefix, five or more internally-generated characters, and the suffix.
+     *
+     * <p> If the <code>directory</code> argument is <code>null</code> then the
+     * system-dependent default temporary-file directory will be used.  The
+     * default temporary-file directory is specified by the system property
+     * <code>java.io.tmpdir</code>.  On UNIX systems the default value of this
+     * property is typically <code>"/tmp"</code> or <code>"/var/tmp"</code>; on
+     * Microsoft Windows systems it is typically <code>"C:\\WINNT\\TEMP"</code>.  A different
+     * value may be given to this system property when the Java virtual machine
+     * is invoked, but programmatic changes to this property are not guaranteed
+     * to have any effect upon the temporary directory used by this method.
+	 *
+	 * @param  prefix is the prefix string to be used in generating the file's
+	 *                    name; must be at least three characters long
+	 *
+	 * @param  suffix is the suffix string to be used in generating the file's
+	 *                    name; may be <code>null</code>, in which case the
+	 *                    suffix <code>".tmp"</code> will be used
+	 * @param  directory is the directory in which the file is to be created, or
+	 *                    <code>null</code> if the default temporary-file
+	 *                    directory is to be used
+	 * @return  An abstract pathname denoting a newly-created empty file
+	 * @throws  IllegalArgumentException
+	 *          If the <code>prefix</code> argument contains fewer than three
+	 *          characters
+	 * @throws  IOException  If a file could not be created
+	 * @throws  SecurityException
+	 *          If a security manager exists and its <code>{@link
+	 *          java.lang.SecurityManager#checkWrite(java.lang.String)}</code>
+	 *          method does not allow a file to be created
+	 * @since 6.2
+	 */
+	public static File createTempDirectory(String prefix, String suffix, File directory) throws IOException {
+		if (prefix == null) throw new NullPointerException();
+		if (prefix.length() < 3)
+			throw new IllegalArgumentException("Prefix string too short"); //$NON-NLS-1$
+		String s = (suffix == null) ? ".tmp" : suffix; //$NON-NLS-1$
+		File targetDirectory;
+		if (directory == null) {
+			targetDirectory = new File(System.getProperty("java.io.tmpdir")); //$NON-NLS-1$
+		}
+		else {
+			targetDirectory = directory;
+		}
+		File f;
+		do {
+			long n = RANDOM.nextLong();
+			if (n == Long.MIN_VALUE) {
+				n = 0;      // corner case
+			}
+			else {
+				n = Math.abs(n);
+			}
+			StringBuffer buffer = new StringBuffer();
+			buffer.append(prefix);
+			buffer.append(Long.toString(n));
+			buffer.append(s);
+			f = new File(targetDirectory, buffer.toString());
+		} 
+		while (!f.mkdirs());
+		return f;
+	}
+
+    /** Hook to recursively delete files on JVM exit. 
 	 * 
 	 * @author $Author: galland$
 	 * @version $FullVersion$
@@ -2585,7 +2707,7 @@
 		public DeleteOnExitHook() {
 			setName("DeleteOnExitHook"); //$NON-NLS-1$
 		}
-		
+
 		/**
 		 * {@inheritDoc}
 		 */
@@ -2606,7 +2728,7 @@
 				}
 			}
 		}
-		
+
 		/** Add a file to delete.
 		 * 
 		 * @param file
@@ -2621,7 +2743,7 @@
 				this.filesToDelete.add(file);
 			}
 		}
-		
+
 		/** Remove a file to delete.
 		 * 
 		 * @param file
@@ -2637,8 +2759,8 @@
 				}
 			}
 		}
-		
+
 	}
-	
+
 }
 


Mail converted by MHonArc 2.6.19+ http://listengine.tuxfamily.org/