.project work
diff --git a/bundles/org.eclipse.team.cvs.core/plugin.properties b/bundles/org.eclipse.team.cvs.core/plugin.properties
index aaa56cf..dec360e 100644
--- a/bundles/org.eclipse.team.cvs.core/plugin.properties
+++ b/bundles/org.eclipse.team.cvs.core/plugin.properties
@@ -1,2 +1,3 @@
 pluginName = CVS Team Provider
-cvsNature=CVS Team Nature
\ No newline at end of file
+cvsNature=CVS Team Nature
+vcmMetaMarker=VCM meta file
\ No newline at end of file
diff --git a/bundles/org.eclipse.team.cvs.core/plugin.xml b/bundles/org.eclipse.team.cvs.core/plugin.xml
index 3d1a367..89aad52 100644
--- a/bundles/org.eclipse.team.cvs.core/plugin.xml
+++ b/bundles/org.eclipse.team.cvs.core/plugin.xml
@@ -60,4 +60,11 @@
 		<ignore pattern = "*.class" selected = "true"/>
 	</extension>
  
+  	<!-- *************** Markers **************** -->
+	<extension id="cvsmarker" point="org.eclipse.core.resources.markers"/>
+	<extension id="vcmmeta" point="org.eclipse.core.resources.markers" name="%vcmMetaMarker">
+		<super type="org.eclipse.core.resources.problemmarker"/>
+		<super type="org.eclipse.team.cvs.core.cvsmarker"/>
+		<persistent value="true"/>
+	</extension>
 </plugin>
\ No newline at end of file
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/FileInputStreamProvider.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/FileInputStreamProvider.java
new file mode 100644
index 0000000..4922c94
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/FileInputStreamProvider.java
@@ -0,0 +1,188 @@
+package org.eclipse.team.internal.ccvs.core.client;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2002.
+ * All Rights Reserved.
+ */
+ 
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.Policy;
+
+/**
+ * This class can be used to transfer a file from the CVS server to a local IFile
+ */
+public class FileInputStreamProvider {
+	
+	// default file transfer buffer size (in bytes)
+	private static int TRANSFER_BUFFER_SIZE = 8192;
+	// update progress bar in increments of this size (in bytes)
+	//   no incremental progress shown for files smaller than this size
+	private static int TRANSFER_PROGRESS_INCREMENT = 32768;
+	
+	// the platform's line termination sequence
+	private static final byte[] PLATFORM_NEWLINE_BYTES =
+		System.getProperty("line.separator").getBytes();  //$NON-NLS-1$ // at least one byte long
+	// the server's line termination sequence
+	private static final int SERVER_NEWLINE_BYTE = 0x0a; // exactly one byte long
+	private static final byte[] SERVER_NEWLINE_BYTES = new byte[] { SERVER_NEWLINE_BYTE };
+	// true iff newlines must be converted between platform and server formats
+	private static boolean DONT_CONVERT_NEWLINES = PLATFORM_NEWLINE_BYTES.length == 1
+		&& PLATFORM_NEWLINE_BYTES[0] == SERVER_NEWLINE_BYTE;
+		
+	// VCM 1.0 comitted files using CR/LF as a delimiter
+	private static final int CARRIAGE_RETURN_BYTE = 0x0d;
+	
+	private InputStream input;
+	private int fileSize;
+	private int totalRead;
+	private boolean isBinary;
+	private IProgressMonitor monitor;
+	private byte[] buffer;
+	private int nextProgressThresh;
+	
+	private static final byte[] BUFFER = new byte[TRANSFER_BUFFER_SIZE / 2];
+	private static final byte[] EXPANSION_BUFFER = new byte[TRANSFER_BUFFER_SIZE];
+	
+	private int position;
+	private int bufferLength;
+	private String title;
+	
+	public FileInputStreamProvider(InputStream input, int fileSize, boolean isBinary, String title, IProgressMonitor monitor) {
+		this.input = input;
+		this.fileSize = fileSize;
+		this.totalRead = 0;
+		this.isBinary = isBinary;
+		this.monitor = monitor;
+		this.buffer = BUFFER;
+		this.nextProgressThresh = TRANSFER_PROGRESS_INCREMENT;
+		this.title = title;
+	}
+	
+	public class InputStreamFromServer extends InputStream {
+		public int read() throws IOException {
+			if (position >= bufferLength) {
+				if (fill() == -1)
+					return -1;
+			}
+			return buffer[position++];
+		}
+		public int read(byte[] bytes) throws IOException {
+			return read(bytes, 0, bytes.length);
+		}
+		public int read(byte[] bytes, int offset, int length) throws IOException {
+			if (position >= bufferLength) {
+				if (fill() == -1)
+					return -1;
+			}
+			length = Math.min(bufferLength - position, length);
+			System.arraycopy(buffer, position, bytes, offset, length);
+			position += length;
+			return length;
+		}
+	}
+	
+	/**
+	 * Return a stream that can be passed to IFile#setContent()
+	 * After the call to setContent, the receiver's input stream will be at the byte
+	 * after the received file.
+	 */
+	public InputStream getInputStream() {
+		return new InputStreamFromServer();
+	}
+	
+	/*
+	 * Transfers a file to or from the remove CVS server, possibly expanding line delimiters.
+	 * <p>
+	 * Line termination sequences are only converted upon request by specifying an
+	 * array containing the expected sequence of bytes representing an outbound newline,
+	 * and a single byte representing an inbound newline.  If null is passed for the
+	 * former, the file is assumed to have binary contents, hence no translation is
+	 * performed.
+	 * </p><p>
+	 * Translation is performed on-the-fly, so the file need not fit in available memory.
+	 * </p>
+	 * @param in the input stream
+	 * @param out the output stream
+	 * @param size the source file size
+	 * @param newlineIn the single byte for a received newline, ignored if binary
+	 * @param newlineOut the sequence of bytes for sent newline, or null if binary
+	 * @param monitor the progress monitor
+	 * @param title the name of the file being received (as shown in the monitor)
+	 */
+	private int fill() throws IOException {
+		
+		// Check if we've read the entire file
+		if (totalRead == fileSize) {
+			return -1;
+		} else if (position < bufferLength) {
+			return bufferLength - position;
+		}
+
+		position = 0;
+
+		// If we're not converting, use the big buffer to read
+		if (isBinary || DONT_CONVERT_NEWLINES) {
+			buffer = EXPANSION_BUFFER;
+		} else {
+			buffer = BUFFER;
+		}
+		
+		bufferLength = input.read(buffer, 0, (int) Math.min(buffer.length, fileSize - totalRead));
+		if (bufferLength == -1) {
+			// Unexpected end of stream
+			throw new IOException(Policy.bind("Session.readError")); //$NON-NLS-1$
+		}
+		totalRead += bufferLength;
+		
+		if (isBinary || DONT_CONVERT_NEWLINES) {
+			return bufferLength;
+		}
+		
+		bufferLength = convertNewLines(BUFFER, EXPANSION_BUFFER, bufferLength);
+		buffer = EXPANSION_BUFFER;
+		
+		// update progress monitor
+		if (totalRead > nextProgressThresh) {
+			monitor.subTask(Policy.bind("Session.transfer", //$NON-NLS-1$
+					new Object[] { title, new Long(totalRead / 1024), new Long(fileSize / 1024)}));
+			nextProgressThresh = totalRead + TRANSFER_PROGRESS_INCREMENT;
+		}
+		
+		return bufferLength;
+	}
+	
+	/*
+	 * Copy the bytes from the source to the target, converting any LF to the platform newline byte.
+	 * 
+	 * There is special handling that will skip incoming CRs that precede LF.
+	 */
+	private int convertNewLines(byte[] source, byte[] target, int length) {
+		boolean seenCR = false;
+		int targetPosition = 0;
+		for (int sourcePosition = 0; sourcePosition < length; ++sourcePosition) {
+			final byte b = source[sourcePosition];
+			if (b == CARRIAGE_RETURN_BYTE) {
+				// We keep track of CRs to perform autocorrection for improperly stored text files
+				seenCR = true;
+			} else {
+				if (b == SERVER_NEWLINE_BYTE) {
+					// if fixCRLF we ignore previous CR (if there was one)
+					// replace newlineIn with newlineOut
+					for (int x = 0; x < PLATFORM_NEWLINE_BYTES.length; ++x) target[targetPosition++] = PLATFORM_NEWLINE_BYTES[x];
+				} else {
+					if (seenCR) target[targetPosition++] = CARRIAGE_RETURN_BYTE; // preserve stray CR's
+					target[targetPosition++] = b;
+				}
+				seenCR = false;
+			}
+		}
+		if (seenCR) target[targetPosition++] = CARRIAGE_RETURN_BYTE;
+		
+		return targetPosition;
+	}
+}
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/EclipseFile.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/EclipseFile.java
index bc7205d..b07b10e 100644
--- a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/EclipseFile.java
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/EclipseFile.java
@@ -74,7 +74,13 @@
 				try {
 					IFile file = getIFile();
 					if (responseType == CREATED || (responseType == UPDATED && ! resource.exists())) {
-						file.create(new ByteArrayInputStream(toByteArray()), false /*force*/, null);
+						if (resource.exists()) {
+							// Special handling for the .project meta-file
+							// XXX This behavior should be restricted to the meta file!
+							file.setContents(new ByteArrayInputStream(toByteArray()), true /*force*/, true /*keep history*/, null);
+						} else {
+							file.create(new ByteArrayInputStream(toByteArray()), false /*force*/, null);
+						}
 					} else if(responseType == UPDATE_EXISTING) {
 						file.setContents(new ByteArrayInputStream(toByteArray()), false /*force*/, keepLocalHistory /*keep history*/, null);
 					} else {
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/OrphanedFolderListener.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/OrphanedFolderListener.java
deleted file mode 100644
index 93407ed..0000000
--- a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/OrphanedFolderListener.java
+++ /dev/null
@@ -1,118 +0,0 @@
-package org.eclipse.team.internal.ccvs.core.util;
-
-/*
- * (c) Copyright IBM Corp. 2000, 2001.
- * All Rights Reserved.
- */
- 
-import org.eclipse.core.resources.IContainer;
-import org.eclipse.core.resources.IFile;
-import org.eclipse.core.resources.IResource;
-import org.eclipse.core.resources.IResourceChangeEvent;
-import org.eclipse.team.ccvs.core.CVSProviderPlugin;
-import org.eclipse.team.ccvs.core.ICVSFile;
-import org.eclipse.team.ccvs.core.ICVSFolder;
-import org.eclipse.team.internal.ccvs.core.CVSException;
-import org.eclipse.team.internal.ccvs.core.Policy;
-import org.eclipse.team.internal.ccvs.core.client.Session;
-import org.eclipse.team.internal.ccvs.core.resources.CVSWorkspaceRoot;
-import org.eclipse.team.internal.ccvs.core.syncinfo.ResourceSyncInfo;
-
-/**
- * Listen for the addition of orphaned subtrees as a result of a copy or move.
- * 
- * Listen in IResourceChangeEvent.PRE_AUTO_BUILD so that other interested parties 
- * (most notably, the file synchronizer) will receive up to date notifications
- */
-public class OrphanedFolderListener extends ResourceDeltaVisitor {
-
-	private void handleOrphanedSubtree(IResource resource) {
-		if (resource.getType() == IResource.FOLDER) {
-			try {
-				ICVSFolder mFolder = CVSWorkspaceRoot.getCVSFolderFor((IContainer)resource);
-				if (mFolder.isCVSFolder() && ! mFolder.isManaged() && mFolder.getParent().isCVSFolder()) {
-					mFolder.unmanage();
-				}
-			} catch (CVSException e) {
-				CVSProviderPlugin.log(e);
-			}
-		}
-	}
-	
-	private void handleDeletedResource(IResource resource) {
-		if (resource.getType() == IResource.FILE) {
-			try {
-				ICVSFile mFile = CVSWorkspaceRoot.getCVSFileFor((IFile)resource);
-				if (mFile.isManaged()) {
-					ResourceSyncInfo info = mFile.getSyncInfo();
-					if (info.isAdded()) {
-						mFile.unmanage();
-					} else {
-						mFile.setSyncInfo(new ResourceSyncInfo(info.getName(), info.DELETED_PREFIX + info.getRevision(), info.getTimeStamp(), info.getKeywordMode(), info.getTag(), info.getPermissions()));
-					}
-				}
-			} catch (CVSException e) {
-				CVSProviderPlugin.log(e);
-			}
-		}
-	}
-	
-	/*
-	 * Handle the case where an added file has the same name as a "cvs removed" file
-	 * by restoring the sync info to what it was before the delete
-	 */
-	private void handleReplacedDeletion(IResource resource) {
-		if (resource.getType() == IResource.FILE) {
-			try {
-				ICVSFile mFile = CVSWorkspaceRoot.getCVSFileFor((IFile)resource);
-				if (mFile.isManaged()) {
-					ResourceSyncInfo info = mFile.getSyncInfo();
-					if (info.isDeleted()) {
-						mFile.setSyncInfo(new ResourceSyncInfo(info.getName(), info.getRevision(), info.getTimeStamp(), info.getKeywordMode(), info.getTag(), info.getPermissions()));
-					}
-				}
-			} catch (CVSException e) {
-				CVSProviderPlugin.log(e);
-			}
-		}
-	}
-	
-	/*
-	 * @see ResourceDeltaVisitor#handleAdded(IResource[])
-	 */
-	protected void handleAdded(IResource[] resources) {
-		for (int i = 0; i < resources.length; i++) {
-			if (resources[i].getType() == IResource.FOLDER) {
-				handleOrphanedSubtree(resources[i]);
-			} else if (resources[i].getType() == IResource.FILE) {
-				handleReplacedDeletion(resources[i]);
-			}
-		}
-	}
-
-	/*
-	 * @see ResourceDeltaVisitor#handleRemoved(IResource[])
-	 */
-	protected void handleRemoved(IResource[] resources) {
-		for (int i = 0; i < resources.length; i++) {
-			handleDeletedResource(resources[i]);
-		}
-	}
-
-	/*
-	 * @see ResourceDeltaVisitor#handleChanged(IResource[])
-	 */
-	protected void handleChanged(IResource[] resources) {
-	}
-
-	/*
-	 * @see ResourceDeltaVisitor#finished()
-	 */
-	protected void finished() {
-	}
-	
-	protected int getEventMask() {
-		return IResourceChangeEvent.PRE_AUTO_BUILD;
-	}
-
-}
\ No newline at end of file
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/ProjectDescriptionManager.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/ProjectDescriptionManager.java
index 7352733..5c902e3 100644
--- a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/ProjectDescriptionManager.java
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/ProjectDescriptionManager.java
@@ -13,6 +13,7 @@
 
 import org.apache.xerces.parsers.SAXParser;
 import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IMarker;
 import org.eclipse.core.resources.IProject;
 import org.eclipse.core.resources.IProjectDescription;
 import org.eclipse.core.resources.IResource;
@@ -45,8 +46,11 @@
 public class ProjectDescriptionManager {
 
 	public final static IPath PROJECT_DESCRIPTION_PATH = new Path(".vcm_meta"); //$NON-NLS-1$
+	public final static IPath CORE_PROJECT_DESCRIPTION_PATH = new Path(".project"); //$NON-NLS-1$
 	public final static boolean UPDATE_PROJECT_DESCRIPTION_ON_LOAD = true;
 
+	public static final String VCMMETA_MARKER = "org.eclipse.team.cvs.core.vcmmeta";
+	
 	/*
 	 * Read the project meta file into the provider description
 	 */
@@ -141,7 +145,7 @@
 					is.close();
 				}
 				try {
-					project.setDescription(desc, progress);
+					project.setDescription(desc, IResource.FORCE | IResource.KEEP_HISTORY, progress);
 				} catch (CoreException ex) {
 					// Failing to set the description is probably due to a missing nature
 					// Other natures are still set
@@ -157,6 +161,10 @@
 						Util.logError(Policy.bind("ProjectDescriptionManager.unableToSetDescription"), ex); //$NON-NLS-1$
 					}
 				}
+				// Mark the .vcm_meta file with a problem marker
+				if (project.getFile(CORE_PROJECT_DESCRIPTION_PATH).exists()) {
+					createVCMMetaMarker(descResource);
+				}
 			} catch(TeamException ex) {
 				Util.logError(Policy.bind("ProjectDescriptionManager.unableToReadDescription"), ex); //$NON-NLS-1$
 				// something went wrong, delete the project description file
@@ -178,7 +186,9 @@
 	public static void writeProjectDescriptionIfNecessary(CVSTeamProvider provider, IResource resource, IProgressMonitor monitor) throws CVSException {
 		if (resource.getType() == IResource.PROJECT || isProjectDescription(resource)) {
 			IProject project = resource.getProject();
-			writeProjectDescription(project, monitor);
+			if (project.getFile(PROJECT_DESCRIPTION_PATH).exists() /* || ! project.getFile(CORE_PROJECT_DESCRIPTION_PATH).exists() */) {
+				writeProjectDescription(project, monitor);
+			}
 		}
 	}
 
@@ -233,4 +243,19 @@
 		};
 		ResourcesPlugin.getWorkspace().addResourceChangeListener(changeListener, IResourceChangeEvent.PRE_AUTO_BUILD);
 	}
+	
+	protected static IMarker createVCMMetaMarker(IResource resource) {
+		try {
+			IMarker[] markers = resource.findMarkers(VCMMETA_MARKER, false, IResource.DEPTH_ZERO);
+   			if (markers.length == 1) {
+   				return markers[0];
+   			}
+			IMarker marker = resource.createMarker(VCMMETA_MARKER);
+			marker.setAttribute(IMarker.MESSAGE, resource.getName() + " file exists in " + resource.getProject().getName() + " but is no longer required and can be deleted");
+			return marker;
+		} catch (CoreException e) {
+			Util.logError("Error creating deletion marker", e);
+		}
+		return null;
+	}
 }
\ No newline at end of file