--- /dev/null
+// CvsDirectory.java\r
+// $Id: CvsDirectory.java,v 1.1 2010/06/15 12:28:49 smhuang Exp $ \r
+// (c) COPYRIGHT MIT and INRIA, 1997.\r
+// Please first read the full copyright statement in file COPYRIGHT.html\r
+\r
+package org.w3c.cvs2;\r
+\r
+import java.util.Enumeration;\r
+import java.util.Hashtable;\r
+import java.util.Properties;\r
+import java.util.StringTokenizer;\r
+\r
+import java.io.DataInputStream;\r
+import java.io.File;\r
+import java.io.FileInputStream;\r
+import java.io.FilenameFilter;\r
+import java.io.FilterInputStream;\r
+import java.io.InputStream;\r
+import java.io.OutputStream;\r
+import java.io.PrintStream;\r
+\r
+import org.w3c.util.AsyncLRUList;\r
+import org.w3c.util.LRUAble;\r
+import org.w3c.util.LRUList;\r
+\r
+//\r
+// FIXME add extra environment parameter to all public methods\r
+// witch run cvs.\r
+//\r
+\r
+public class CvsDirectory implements LRUAble, CVS {\r
+ /**\r
+ * Property giving the path of the cvs binary.\r
+ * This property should be set to the absolute path to the cvs command\r
+ * in your local environment.\r
+ * <p>This property defaults to <code>/usr/local/bin/cvs</code>.\r
+ */\r
+ public static final String CVSPATH_P = "org.w3c.cvs.path" ;\r
+ /**\r
+ * Property giving your CVS repository.\r
+ * This property should be set to the absolute path of your repository.\r
+ * <p>This property defaults to <code>/afs/w3.org/pub/WWW</code>.\r
+ */\r
+ public static final String CVSROOT_P = "org.w3c.cvs.root" ;\r
+ /**\r
+ * Property giving the path of the cvswrapper.\r
+ * Because CVS can't run without being in the right directory, this\r
+ * classes use a shell script wrapper to issue cvs commands, that will\r
+ * change directory appropriately.\r
+ * <p>You should have gotten this wrapper in the distribution \r
+ * <code>bin</code> directory.\r
+ * <p>This property defaults to \r
+ * <code>/afs/w3.org/usr/abaird/Jigsaw/bin/cvs_wrapper</code>.\r
+ */\r
+ public final static String CVSWRAP_P = "org.w3c.cvs.wrapper" ;\r
+\r
+ /**\r
+ * The default CVS path.\r
+ */\r
+ public final static String cvspath_def \r
+ = "/usr/local/bin/cvs" ;\r
+ /**\r
+ * The default CVS root path.\r
+ */\r
+ public final static String cvsroot_def \r
+ = "/afs/w3.org/CVS-Repository";\r
+ /**\r
+ * The default CVS wrapper path.\r
+ */\r
+ public final static String cvswrap_def\r
+ = "/afs/w3.org/usr/abaird/Jigsaw/bin/cvs_wrapper";\r
+\r
+ /**\r
+ * Our cache of existing CVS managers.\r
+ * Maps absolute directory names to appropriate CvsDirectory instance.\r
+ */\r
+ protected static Hashtable cache = new Hashtable(23);\r
+ /**\r
+ * All CVS entries are LRU maintained too.\r
+ */\r
+ protected static LRUList lru = new AsyncLRUList();\r
+ /**\r
+ * Our recommended cache size.\r
+ */\r
+ protected static int cachesize = 32;\r
+\r
+ /**\r
+ * LRU management - previous entry.\r
+ */\r
+ protected LRUAble prev = null;\r
+ /**\r
+ * LRU management - next entry.\r
+ */\r
+ protected LRUAble next = null;\r
+ /**\r
+ * The time at which we last checked the cvs status of that directory.\r
+ */\r
+ protected long cvscheck_stamp = -1;\r
+ /**\r
+ * The time at which we last examined the repository for this directory.\r
+ */\r
+ protected long cvsrep_stamp = -1;\r
+ /**\r
+ * Has this directory manager been cleaned up (removed from cache).\r
+ */\r
+ protected boolean clean = true;\r
+ /**\r
+ * The directory we manage.\r
+ */\r
+ protected File directory = null;\r
+ /**\r
+ * The corresponding repository directory (when available).\r
+ */\r
+ protected File repdir = null;\r
+ /**\r
+ * Known CVS entries (files)\r
+ */\r
+ protected Hashtable entries = null;\r
+ /**\r
+ * Our associated CvsRunner.\r
+ */\r
+ protected CvsRunner runner = null;\r
+ /**\r
+ * The properties we use for initialization.\r
+ */\r
+ public Properties props = null;\r
+\r
+ String cvspath = cvspath_def ;\r
+ String cvsroot = cvsroot_def ;\r
+ String cvswrapper[] = { cvswrap_def } ;\r
+\r
+ \r
+ /**\r
+ * This one is the third copy of the same piece of code (!)\r
+ * Parse the given prop value into an array of | separated components.\r
+ * @param propval The property value (may be <strong>null</strong>).\r
+ * @param def The default value (if undefined).\r
+ * @return A String array, or <strong>null</strong>.\r
+ */\r
+\r
+ private static String[] parseArrayProperty(String propval) {\r
+ if ( propval == null )\r
+ return null;\r
+ // Parse the property value:\r
+ StringTokenizer st = new StringTokenizer(propval, "|");\r
+ int len = st.countTokens();\r
+ String ret[] = new String[len];\r
+ for (int i = 0 ; i < ret.length ; i++) {\r
+ ret[i] = st.nextToken();\r
+ }\r
+ return ret;\r
+ }\r
+\r
+ File computeRepositoryDirectory(File dir) {\r
+ File rep = new File(new File(dir, "CVS"), "Repository");\r
+ File ret = null;\r
+ if ( ! rep.exists() )\r
+ return null;\r
+ try {\r
+ DataInputStream in = new DataInputStream(new FileInputStream(rep));\r
+ String nm = in.readLine();\r
+ if ( nm.startsWith("/") )\r
+ ret = new File(nm);\r
+ else\r
+ ret = new File(cvsroot, nm);\r
+ in.close();\r
+ } catch (Exception ex) {\r
+ return null;\r
+ }\r
+ return ret;\r
+ }\r
+\r
+ protected String[] getCvsWrapper() {\r
+ return cvswrapper;\r
+ }\r
+\r
+ protected String[] getCvsDefaults() {\r
+ String ret[] = new String[6];\r
+ ret[0] = "-directory";\r
+ ret[1] = getDirectory().getAbsolutePath();\r
+ ret[2] = cvspath;\r
+ ret[3] = "-q";\r
+ ret[4] = "-d";\r
+ ret[5] = cvsroot;\r
+ return ret;\r
+ }\r
+\r
+ protected synchronized void createFileEntry(long timestamp\r
+ , String name\r
+ , int status) {\r
+ if ( entries == null )\r
+ entries = new Hashtable(13);\r
+ CvsEntry entry = new CvsEntry(this, timestamp, name, false, status);\r
+ entries.put(name, entry);\r
+ }\r
+\r
+ protected synchronized void createDirectoryEntry(long timestamp\r
+ , String name\r
+ , int status) {\r
+ if ( entries == null ) \r
+ entries = new Hashtable(13);\r
+ CvsEntry entry = new CvsEntry(this, timestamp, name, true, status);\r
+ entries.put(name, entry);\r
+ }\r
+\r
+ public static Properties defprops = null;\r
+\r
+ /**\r
+ * Get a CvsDirectory.\r
+ * @param directory The CVS directory.\r
+ * @param props The cvs properties.\r
+ * @param cvspath The absolute path of the cvs program.\r
+ * @param cvsroot The absolute path of the CVS repository. \r
+ * @param cvswrap The absolute path of the cvs wrapper program\r
+ * @return A CvsDirectory instance.\r
+ * @exception CvsException If some initialisation failed\r
+ */\r
+ public static synchronized CvsDirectory getManager(File directory,\r
+ Properties props,\r
+ String cvspath,\r
+ String cvsroot,\r
+ String cvswrap[])\r
+ throws CvsException\r
+ {\r
+ // Initialize default properties if not done yet:\r
+ if ( defprops == null )\r
+ defprops = System.getProperties();\r
+ String abspath = directory.getAbsolutePath();\r
+ // Check the cache first:\r
+ CvsDirectory cvs = (CvsDirectory) cache.get(abspath);\r
+ if ( cvs != null ) {\r
+ cvs.cacheLoaded();\r
+ return cvs;\r
+ }\r
+ // Create a new cache entry for that directory and add to cache:\r
+ cvs = new CvsDirectory(directory\r
+ , ((props == null) ? defprops : props)\r
+ , cvspath\r
+ , cvsroot\r
+ , cvswrap);\r
+ if ( cache.size() >= cachesize )\r
+ cacheUnload();\r
+ cache.put(abspath, cvs);\r
+ lru.toHead(cvs);\r
+ return cvs;\r
+ }\r
+\r
+ /**\r
+ * Get a CvsDirectory.\r
+ * @param directory The CVS directory\r
+ * @return A CvsDirectory instance.\r
+ * @exception CvsException If some initialisation failed\r
+ */\r
+ public static CvsDirectory getManager(File directory) \r
+ throws CvsException\r
+ {\r
+ return getManager(directory, null, null, null, null);\r
+ }\r
+\r
+ /**\r
+ * @param directory The CVS directory.\r
+ * @param props The cvs properties.\r
+ * @return A CvsDirectory instance.\r
+ * @exception CvsException If some initialisation failed\r
+ */\r
+ public static CvsDirectory getManager(File directory , Properties props) \r
+ throws CvsException\r
+ {\r
+ return getManager(directory, props, null, null, null);\r
+ }\r
+\r
+ /**\r
+ * @param father The father CvsDirectory.\r
+ * @param directory The CVS directory.\r
+ * @return A CvsDirectory instance.\r
+ * @exception CvsException If some initialisation failed\r
+ */\r
+ protected static CvsDirectory getManager(CvsDirectory father, File dir) \r
+ throws CvsException\r
+ {\r
+ return getManager(dir, father.props, null, null, null);\r
+ }\r
+\r
+ /**\r
+ * LRU management - Get next node.\r
+ * @return A CvsDirectory instance.\r
+ */\r
+\r
+ public LRUAble getNext() {\r
+ return next;\r
+ }\r
+\r
+ /**\r
+ * LRU management - Get previous node.\r
+ * @return A CvsDirectory instance.\r
+ */\r
+\r
+ public LRUAble getPrev() {\r
+ return prev;\r
+ }\r
+\r
+ /**\r
+ * LRU management - Set next node.\r
+ * @return A CvsDirectory instance.\r
+ */\r
+\r
+ public void setNext(LRUAble next) {\r
+ this.next = next;\r
+ }\r
+\r
+ /**\r
+ * LRU management - Set previous node.\r
+ * @return A CvsDirectory instance.\r
+ */\r
+\r
+ public void setPrev(LRUAble prev) {\r
+ this.prev = prev;\r
+ }\r
+\r
+ public static String statusToString(int st) {\r
+ return (((st > 0) && (st < status.length))\r
+ ? status[st]\r
+ : "unknown");\r
+ }\r
+\r
+ /**\r
+ * This directory manager is being fetched from the cache.\r
+ * Perform any action before we return it back to the user.\r
+ * @exception CvsException If some CVS action fails during \r
+ * reinitialization.\r
+ */\r
+\r
+ protected void cacheLoaded() \r
+ throws CvsException\r
+ {\r
+ lru.toHead(this);\r
+ }\r
+\r
+ /**\r
+ * Remove a directory manager from the cache.\r
+ * Clear al references to other objects in order to free up memory\r
+ * even if the caller maintains a pointer to the manager.\r
+ */\r
+\r
+ protected static synchronized void cacheUnload() {\r
+ // Pick the manager to remove:\r
+ CvsDirectory cvs = (CvsDirectory) lru.removeTail();\r
+ // Clean it up:\r
+ if ( cvs != null ) {\r
+ cvs.entries = null;\r
+ cvs.clean = true;\r
+ cvs.cvsrep_stamp = -1;\r
+ cvs.cvscheck_stamp = -1;\r
+ cache.remove(cvs.getDirectory().getAbsolutePath());\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Look for a file entry.\r
+ * @param filename The name of the entry to look for.\r
+ */\r
+\r
+ protected CvsEntry getFileEntry(String filename) {\r
+ return (entries != null) ? (CvsEntry) entries.get(filename) : null ;\r
+ }\r
+\r
+ protected void removeFileEntry(String filename) {\r
+ if (entries != null) entries.remove(filename);\r
+ }\r
+\r
+ /**\r
+ * Look for a sub-directory entry.\r
+ * @param filename The name of the entry to look for.\r
+ */\r
+\r
+ protected CvsEntry getDirectoryEntry(String filename) {\r
+ return (entries != null) ? (CvsEntry) entries.get(filename) : null ;\r
+ }\r
+\r
+ /**\r
+ * Refresh the file entries for that directory.\r
+ * @exception CvsException If the underlying CVS command failed.\r
+ */\r
+\r
+ public void refresh() \r
+ throws CvsException\r
+ {\r
+ synchronized(this) {\r
+ LoadUpdateHandler handler = new LoadUpdateHandler(this);\r
+ CvsStatusHandler statush = new CvsStatusHandler(this);\r
+ try {\r
+ entries = null; //FIXME other effect?\r
+ runner.cvsLoad(this, handler, statush);\r
+ } catch (CvsException ex) {\r
+ clean = true;\r
+ entries = null;\r
+ throw ex;\r
+ }\r
+ clean = false;\r
+ cvscheck_stamp = getDirectory().lastModified();\r
+ handler.notifyEnd();\r
+ //must be called after UpdateHandler\r
+ statush.notifyEnd();\r
+ }\r
+ lru.toHead(this);\r
+ }\r
+\r
+ /**\r
+ * Refresh the file entries for that filename.\r
+ * @exception CvsException If the underlying CVS command failed.\r
+ */\r
+ protected void refresh(String filename) \r
+ throws CvsException\r
+ {\r
+ synchronized(this) {\r
+ LoadUpdateHandler handler = new LoadUpdateHandler(this);\r
+ CvsStatusHandler statush = new CvsStatusHandler(this);\r
+\r
+ runner.cvsLoad(this, filename, handler, statush);\r
+\r
+ handler.notifyEnd();\r
+ //must be called after UpdateHandler\r
+ statush.notifyEnd();\r
+ }\r
+ lru.toHead(this);\r
+ }\r
+\r
+ /**\r
+ * Refresh the file entry status for that filename.\r
+ * @exception CvsException If the underlying CVS command failed.\r
+ */\r
+ protected void refreshStatus(String filename) \r
+ throws CvsException\r
+ {\r
+ synchronized(this) {\r
+ LoadUpdateHandler handler = new LoadUpdateHandler(this);\r
+ runner.cvsLoad(this, filename, handler);\r
+ handler.notifyEnd();\r
+ }\r
+ lru.toHead(this);\r
+ }\r
+\r
+ /**\r
+ * Refresh the file entry revision number for that filename.\r
+ * @exception CvsException If the underlying CVS command failed.\r
+ */\r
+ protected void refreshRevision(String filename) \r
+ throws CvsException\r
+ {\r
+ synchronized(this) {\r
+ CvsStatusHandler statush = new CvsStatusHandler(this);\r
+ runner.cvsStatus(this, filename, statush);\r
+ statush.notifyEnd();\r
+ }\r
+ }\r
+ \r
+ /**\r
+ * This directory manager is about to be used, check it.\r
+ * @exception CvsException If some CVS error occurs in that process.\r
+ */\r
+\r
+ protected synchronized void checkUse()\r
+ throws CvsException\r
+ {\r
+ if ( clean ) {\r
+ // This has been cleaned up some times ago, restore:\r
+ if ( cache.size() >= cachesize )\r
+ cacheUnload();\r
+ cacheLoaded();\r
+ cache.put(getDirectory().getAbsolutePath(), this);\r
+ clean = false;\r
+ } \r
+ // Check if update is needed:\r
+ if ( needsUpdate() ) \r
+ refresh();\r
+ }\r
+\r
+ /**\r
+ * That file in the directory is about to be used, check its status.\r
+ * @param filename The name of the entry that is about to be used.\r
+ * @exception CvsException If a CVS error occurs.\r
+ */\r
+\r
+ protected synchronized void checkUse(String filename)\r
+ throws CvsException\r
+ {\r
+ CvsEntry entry = getFileEntry(filename);\r
+ if ((entry == null) || entry.needsUpdate())\r
+ refresh();\r
+ }\r
+\r
+ /**\r
+ * This directory manager is about to be used for sub-directories.\r
+ * @exception CvsException If a CVS error occurs.\r
+ */\r
+ protected synchronized void checkDirectoryUse()\r
+ throws CvsException\r
+ {\r
+ //test new thing\r
+ //runner.cvsUpdateDirectories(this, null);\r
+\r
+ // Can we gain access to the repository ?\r
+ if (repdir == null) {\r
+ if ((repdir = computeRepositoryDirectory(getDirectory())) == null)\r
+ throw new CvsException("Repository not accessible.");\r
+ }\r
+ // Are we uptodate against repository ?\r
+ if ( cvsrep_stamp < repdir.lastModified() ) {\r
+ long stamp = System.currentTimeMillis();\r
+ // Get all subdirs from repository:\r
+ String subdirs[] = repdir.list(new DirectoryFilter());\r
+ if ( subdirs != null ) {\r
+ for (int i = 0 ; i < subdirs.length ; i++) {\r
+ File subdir = new File(getDirectory(), subdirs[i]);\r
+ if ( subdir.exists() )\r
+ createDirectoryEntry(stamp, subdirs[i], DIR_CO);\r
+ else\r
+ createDirectoryEntry(stamp, subdirs[i], DIR_NCO);\r
+ }\r
+ }\r
+ // Check against all local sub-directories:\r
+ subdirs = getDirectory().list(new DirectoryFilter());\r
+ if ( subdirs != null ) {\r
+ for (int i = 0 ; i < subdirs.length ; i++) {\r
+ if (getDirectoryEntry(subdirs[i]) == null)\r
+ createDirectoryEntry(stamp, subdirs[i], DIR_Q);\r
+ }\r
+ }\r
+ cvsrep_stamp = stamp;\r
+ }\r
+ }\r
+\r
+ /**\r
+ * This directory manager is about to be used for the given sub-directory.\r
+ * (Really really faster than checkDirectoryUse())\r
+ * @exception CvsException If a CVS error occurs.\r
+ */\r
+ protected synchronized void checkDirectoryUse(String subdir) \r
+ throws CvsException\r
+ {\r
+ // Can we gain access to the repository ?\r
+ if (repdir == null) {\r
+ if ((repdir = computeRepositoryDirectory(getDirectory())) == null)\r
+ throw new CvsException("Repository not accessible.");\r
+ }\r
+ // Are we uptodate against repository ?\r
+ if ( cvsrep_stamp < repdir.lastModified() ) {\r
+ long stamp = System.currentTimeMillis();\r
+ File dir = new File (repdir, subdir);\r
+ if (dir.exists()) {\r
+ File subd = new File(getDirectory(), subdir);\r
+ if ( subd.exists() )\r
+ createDirectoryEntry(stamp, subdir, DIR_CO);\r
+ else\r
+ createDirectoryEntry(stamp, subdir, DIR_NCO);\r
+ } else {\r
+ createDirectoryEntry(stamp, subdir, DIR_Q);\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Does this directory needs some CVS update ?\r
+ */\r
+\r
+ protected boolean needsUpdate() {\r
+ // Has the directory changed since we last visited it ?\r
+ if (cvscheck_stamp < directory.lastModified()) {\r
+ return true;\r
+ }\r
+ // Has any entry changed since its last status ?\r
+ if ( entries != null ) {\r
+ Enumeration e = entries.elements();\r
+ while ( e.hasMoreElements() ) {\r
+ CvsEntry entry = (CvsEntry) e.nextElement();\r
+ if ( entry.needsUpdate() ) {\r
+ return true;\r
+ }\r
+ }\r
+ }\r
+ return false;\r
+ }\r
+\r
+ /**\r
+ * List all available file entries in that directory.\r
+ * This method will list all possible files:\r
+ * <ul><li>The ones that exist but are not in the repository.\r
+ * <li>The ones that are in the repository but not in local space.\r
+ * <li>All the other ones.\r
+ * </ul>\r
+ * @return An enumeration listing zero or more String instances.\r
+ * @exception CvsException If some CVS error occured while examining\r
+ * the cvs status of entries.\r
+ */\r
+\r
+ public Enumeration listFiles()\r
+ throws CvsException\r
+ {\r
+ lru.toHead(this);\r
+ checkUse();\r
+ return new FileEnumeration(entries);\r
+ }\r
+\r
+ /**\r
+ * List available sub-directories of that directory.\r
+ * This method will list of possible directories. If access to the\r
+ * repository is permitted, it will look into that to get a list of\r
+ * unchecked out directories, otherwise, it will just list the \r
+ * checked out ones.\r
+ * @return An enumeration listing zero or more File instances.\r
+ * @exception CvsException If some CVS error occured while examining\r
+ * the cvs status of entries.\r
+ */\r
+\r
+ public Enumeration listDirectories() \r
+ throws CvsException\r
+ {\r
+ lru.toHead(this);\r
+ checkDirectoryUse();\r
+ return new DirectoryEnumeration(entries);\r
+ }\r
+\r
+ /**\r
+ * Get the status of a file entry.\r
+ * @param filename The file whose status is to be fetched.\r
+ * @param refresh Should we refresh the status?\r
+ * @return A integer status indicating the CVS status of that file within\r
+ * the repository.\r
+ * @exception CvsException If some CVS error occured while examining\r
+ * the cvs status of entries.\r
+ */\r
+ public int status(String filename, boolean refresh) \r
+ throws CvsException\r
+ {\r
+ if (refresh)\r
+ refreshStatus(filename);\r
+ return status(filename);\r
+ }\r
+\r
+ /**\r
+ * Get the status of a file entry.\r
+ * @param filename The file whose status is to be fetched.\r
+ * @return A integer status indicating the CVS status of that file within\r
+ * the repository.\r
+ * @exception CvsException If some CVS error occured while examining\r
+ * the cvs status of entries.\r
+ */\r
+ public int status(String filename) \r
+ throws CvsException\r
+ {\r
+ lru.toHead(this);\r
+ checkUse(filename);\r
+ CvsEntry entry = \r
+ (entries != null) ? (CvsEntry) entries.get(filename) : null;\r
+ if ( entry == null ) {\r
+ //let's make a try with the filename in update\r
+ refresh(filename);\r
+ entry = \r
+ (entries != null) ? (CvsEntry) entries.get(filename) : null;\r
+ if ( entry == null )\r
+ throw new CvsException(filename+": no such entry.");\r
+ }\r
+ return entry.getStatus();\r
+ }\r
+\r
+ /**\r
+ * Get the revision number of the given file.\r
+ * @param filename The File name\r
+ * @return A String instance\r
+ * @exception CvsException if some CVS errors occurs\r
+ */\r
+ public String revision(String filename)\r
+ throws CvsException\r
+ {\r
+ lru.toHead(this);\r
+ checkUse(filename);\r
+ CvsEntry entry = \r
+ (entries != null) ? (CvsEntry) entries.get(filename) : null;\r
+ if ( entry == null ) {\r
+ //let's make a try with the filename in update\r
+ refresh(filename);\r
+ entry = \r
+ (entries != null) ? (CvsEntry) entries.get(filename) : null;\r
+ if ( entry == null )\r
+ throw new CvsException(filename+": no such entry.");\r
+ } else if (( entry.getRevision() == null) && \r
+ (entry.getStatus() != FILE_Q)) {\r
+ refreshRevision(filename);\r
+ }\r
+ return entry.getRevision();\r
+ }\r
+\r
+ /**\r
+ * Get the Sticky Options of the given file.\r
+ * @param filename The File name\r
+ * @return A String instance\r
+ * @exception CvsException if some CVS errors occurs\r
+ */\r
+ public String stickyOptions(String filename)\r
+ throws CvsException\r
+ {\r
+ lru.toHead(this);\r
+ checkUse(filename);\r
+ CvsEntry entry = \r
+ (entries != null) ? (CvsEntry) entries.get(filename) : null;\r
+ if ( entry == null ) {\r
+ //let's make a try with the filename in update\r
+ refresh(filename);\r
+ entry = \r
+ (entries != null) ? (CvsEntry) entries.get(filename) : null;\r
+ if ( entry == null )\r
+ throw new CvsException(filename+": no such entry.");\r
+ } else if (( entry.getRevision() == null) && \r
+ (entry.getStatus() != FILE_Q)) {\r
+ refreshRevision(filename);\r
+ }\r
+ return entry.getStickyOptions();\r
+ } \r
+\r
+\r
+ /**\r
+ * Update these files from the repository.\r
+ * @param files The name of the files to update (as a String array).\r
+ * @exception CvsException If CVS process failed.\r
+ */\r
+\r
+ public void update(String files[]) \r
+ throws CvsException\r
+ {\r
+ lru.toHead(this);\r
+ // No need to checkUse here:\r
+ CvsUpdateHandler handler = new CvsUpdateHandler(this);\r
+ runner.cvsUpdate(this, files, handler);\r
+ }\r
+\r
+ /**\r
+ * Update this file from the repository.\r
+ * @param file The name of the file to update (as a String).\r
+ * @exception CvsException If CVS process failed.\r
+ */\r
+ public void update(String file) \r
+ throws CvsException\r
+ {\r
+ String files[] = new String[1];\r
+ files[0] = file;\r
+ update(files);\r
+ }\r
+\r
+ /**\r
+ * Update all that directory's content. (not recursivly).\r
+ * @exception CvsException If some CVS error occured during update.\r
+ */\r
+\r
+ public void update() \r
+ throws CvsException\r
+ {\r
+ lru.toHead(this);\r
+ CvsUpdateHandler handler = new CvsUpdateHandler(this);\r
+ runner.cvsUpdate(this, handler);\r
+ }\r
+\r
+ /**\r
+ * Update thes files matching the given regular expression from the \r
+ * repository.\r
+ * @param files The name of the files to update (as a String array).\r
+ * @exception CvsException If CVS process failed.\r
+ */\r
+ public void updateRegexp(String regexp) \r
+ throws CvsException\r
+ {\r
+ update(regexp);\r
+ }\r
+\r
+ /**\r
+ * Perform a cvs get\r
+ * @param path the file (or directory) path\r
+ * @exception CvsException If CVS process failed.\r
+ */\r
+ public void get(String path) \r
+ throws CvsException \r
+ {\r
+ lru.toHead(this);\r
+ runner.cvsGet(this, path);\r
+ }\r
+\r
+ /**\r
+ * Commit pending actions on given file.\r
+ * @param names The name of the files to commit.\r
+ * @param msg The associated message.\r
+ * @param env The extra env to use during commit.\r
+ * @exception CvsException If some error occured during the CVS process.\r
+ */\r
+ public void commit(String names[], String msg, String env[])\r
+ throws CvsException\r
+ {\r
+ lru.toHead(this);\r
+ // We don't really need to check use here.\r
+ CvsCommitHandler handler = new CvsCommitHandler(this);\r
+ runner.cvsCommit(this, names, msg, handler, env);\r
+ }\r
+\r
+ /**\r
+ * Commit pending actions on given file.\r
+ * @param file The file to commit.\r
+ * @param msg The associated message.\r
+ * @param env The extra env to use during commit.\r
+ * @exception CvsException If some error occured during the CVS process.\r
+ */\r
+ public void commit(String file, String msg, String env[]) \r
+ throws CvsException\r
+ {\r
+ lru.toHead(this);\r
+ String names[] = new String[1];\r
+ names[0] = file;\r
+ commit(names, msg, env);\r
+ }\r
+\r
+ /**\r
+ * Commit pending actions on given file.\r
+ * @param names The name of the files to commit.\r
+ * @param msg The associated message.\r
+ * @exception CvsException If some error occured during the CVS process.\r
+ */\r
+ public void commit(String names[], String msg)\r
+ throws CvsException\r
+ {\r
+ lru.toHead(this);\r
+ // We don't really need to check use here.\r
+ CvsCommitHandler handler = new CvsCommitHandler(this);\r
+ runner.cvsCommit(this, names, msg, handler);\r
+ }\r
+\r
+ /**\r
+ * Commit pending actions on given file.\r
+ * @param file The file to commit.\r
+ * @param msg The associated message.\r
+ * @exception CvsException If some error occured during the CVS process.\r
+ */\r
+ public void commit(String file, String msg) \r
+ throws CvsException\r
+ {\r
+ String names[] = new String[1];\r
+ names[0] = file;\r
+ commit(names, msg);\r
+ }\r
+\r
+ /**\r
+ * Commit pending actions to all that directory content.\r
+ * @param msg The associated message.\r
+ * @exception CvsException If some CVS error occurs during the CVS process.\r
+ */\r
+\r
+ public void commit(String msg)\r
+ throws CvsException\r
+ {\r
+ lru.toHead(this);\r
+ // No need to checkUse\r
+ CvsCommitHandler handler = new CvsCommitHandler(this);\r
+ runner.cvsCommit(this, msg, handler);\r
+ }\r
+\r
+ /**\r
+ * Commit pending actions to all that directory content.\r
+ * @param msg The associated message.\r
+ * @param env The extra environment.\r
+ * @exception CvsException If some CVS error occurs during the CVS process.\r
+ */\r
+\r
+ public void commit(String msg, String env[])\r
+ throws CvsException\r
+ {\r
+ lru.toHead(this);\r
+ // No need to checkUse\r
+ CvsCommitHandler handler = new CvsCommitHandler(this);\r
+ runner.cvsCommit(this, msg, handler, env);\r
+ }\r
+\r
+ /**\r
+ * Commit pending actions on files matching the given regular expression.\r
+ * @param regexp The regular expresion. \r
+ * @param msg The associated message.\r
+ * @param env The extra env to use during commit.\r
+ * @exception CvsException If some error occured during the CVS process.\r
+ */\r
+ public void commitRegexp(String regexp, String comment, String env[]) \r
+ throws CvsException\r
+ {\r
+ commit(regexp, comment, env);\r
+ }\r
+\r
+ /**\r
+ * Revert the file, make the given revision the current one.\r
+ * <UL><LI>First remove the file</LI>\r
+ * <LI>perform a cvs update -p -r <revision></LI>\r
+ * @param filename The name of the file to revert.\r
+ * @param revision The revision number to get.\r
+ * @param msg The associated message.\r
+ * @param env The extra environment.\r
+ * @exception CvsException If some CVS error occurs during the CVS process.\r
+ */\r
+ public void revert(String filename,\r
+ String revision,\r
+ String msg,\r
+ String env[])\r
+ throws CvsException\r
+ {\r
+ File file = new File( getDirectory(), filename);\r
+ if (!file.exists())\r
+ throw new CvsException("the file "+file+" can't be reverted : "+\r
+ "it doesn't exists!");\r
+ // 1: remove the file\r
+ file.delete();\r
+ String names[] = new String[1];\r
+ names[0] = filename;\r
+ // 2: revert \r
+ runner.cvsRevert(this, filename, revision, file, env);\r
+ // done!\r
+ }\r
+\r
+ /**\r
+ * Revert the file, make the given revision the current one.\r
+ * <UL><LI>First remove the file</LI>\r
+ * <LI>perform a cvs update -p -r <revision></LI>\r
+ * @param filename The name of the file to revert.\r
+ * @param revision The revision number to get.\r
+ * @param out The output stream. (reverted file will be written in)\r
+ * @param env The extra environment.\r
+ * @exception CvsException If some CVS error occurs during the CVS process.\r
+ */\r
+ public void revert(String filename, \r
+ OutputStream out, \r
+ String revision,\r
+ String env[]) \r
+ throws CvsException \r
+ {\r
+ runner.cvsRevert(this, filename, revision, out, env);\r
+ }\r
+\r
+ /**\r
+ * Get the log associated to the given file.\r
+ * @param filename The name of the file whose log is to be fetched.\r
+ * @exception CvsException If some CVS error occurs in the process.\r
+ */\r
+\r
+ public String log(String filename)\r
+ throws CvsException\r
+ {\r
+ lru.toHead(this);\r
+ // No need to checkUse here\r
+ return runner.cvsLog(this, filename);\r
+ }\r
+\r
+ /**\r
+ * Get the diff of given file against repository.\r
+ * @param filename The name of the file to diff.\r
+ * @return The diffs has a String, or <strong>null</strong> if the file\r
+ * is in sync with the repository.\r
+ * @exception CvsException If some CVS exception occurs within the\r
+ * process.\r
+ */\r
+\r
+ public String diff(String filename)\r
+ throws CvsException\r
+ {\r
+ lru.toHead(this);\r
+ checkUse(filename);\r
+ CvsEntry entry = getFileEntry(filename);\r
+ // Spare a cvs command when possible:\r
+ if ((entry != null) \r
+ && ( ! entry.needsUpdate())\r
+ && (entry.getStatus() == FILE_OK))\r
+ return null;\r
+ // Have to run the command:\r
+ return runner.cvsDiff(this, filename);\r
+ }\r
+\r
+ /**\r
+ * Add the given file to the CVS repository.\r
+ * The addition will have to be commited through a commit of the same\r
+ * file before taking effect.\r
+ * @param names The name of the files to add.\r
+ * @exception CvsException If some CVS error occurs during the process.\r
+ */\r
+\r
+ public void add(String names[]) \r
+ throws CvsException\r
+ {\r
+ add(names, null);\r
+ }\r
+\r
+ /**\r
+ * Add the given file to the CVS repository.\r
+ * The addition will have to be commited through a commit of the same\r
+ * file before taking effect.\r
+ * @param names The name of the files to add.\r
+ * @param env The extra env to use during the process.\r
+ * @exception CvsException If some CVS error occurs during the process.\r
+ */\r
+ public void add(String names[], String env[]) \r
+ throws CvsException\r
+ {\r
+ lru.toHead(this);\r
+ // No need to checkUse here:\r
+ long stamp = System.currentTimeMillis();\r
+ runner.cvsAdd(this, names, env);\r
+ // Update all files status:\r
+ for (int i = 0 ; i < names.length; i++) {\r
+ CvsEntry entry = getFileEntry(names[i]);\r
+ if ( entry != null )\r
+ entry.setStatus(stamp, FILE_A);\r
+ else\r
+ createFileEntry(stamp, names[i], FILE_A);\r
+ } \r
+ } \r
+\r
+ /**\r
+ * Add the file matching the given regular expression to the CVS \r
+ * repository.\r
+ * The addition will have to be commited through a commit of the same\r
+ * file before taking effect.\r
+ * @param regexp The regular expression.\r
+ * @param env The extra env to use during the process.\r
+ * @exception CvsException If some CVS error occurs during the process.\r
+ */\r
+ public void addRegexp(String regexp, String env[]) \r
+ throws CvsException\r
+ {\r
+ lru.toHead(this);\r
+ String files[] = new String[1];\r
+ files[0] = regexp;\r
+ runner.cvsAdd(this, files, env);\r
+ refresh();\r
+ }\r
+\r
+ /**\r
+ * Remove the given file from the repository.\r
+ * @param names The files to be removed.\r
+ * @exception CvsException If some CVS error occurs during that process.\r
+ */\r
+\r
+ public void remove(String names[], String msg, String env[])\r
+ throws CvsException\r
+ {\r
+ lru.toHead(this);\r
+ // No need to check use here:\r
+ long stamp = System.currentTimeMillis();\r
+ runner.cvsRemove(this, names);\r
+ //commit the remove\r
+ commit(names, msg, env);\r
+ // Update all files status:\r
+ for (int i = 0 ; i < names.length; i++) {\r
+ removeFileEntry(names[i]);\r
+ }\r
+// for (int i = 0 ; i < names.length; i++) {\r
+// CvsEntry entry = getFileEntry(names[i]);\r
+// if ( entry != null )\r
+// entry.setStatus(stamp, FILE_R);\r
+// else\r
+// createFileEntry(stamp, names[i], FILE_R);\r
+// }\r
+ }\r
+\r
+ /**\r
+ * Perform a RCS request.\r
+ * @param command the rcs command splitted in a string array.\r
+ * @exception CvsException If some CVS error occurs during that process.\r
+ */\r
+ public void admin(String command[]) \r
+ throws CvsException\r
+ {\r
+ lru.toHead(this);\r
+ runner.cvsAdmin(this, command);\r
+ }\r
+\r
+ /**\r
+ * Display the status of the entries in that directory.\r
+ * @param prt A print stream to print to.\r
+ */\r
+\r
+ public void display(PrintStream prt) {\r
+ lru.toHead(this);\r
+ try {\r
+ Enumeration files = listFiles();\r
+ while ( files.hasMoreElements() ) {\r
+ String name = (String) files.nextElement();\r
+ int stat = status(name);\r
+ prt.println(name+": "+status[stat]);\r
+ }\r
+ } catch (CvsException ex) {\r
+ prt.println("*** CVS Error: "+ex.getMessage());\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Get a sub-directory status.\r
+ * @param subdir The name of the subdirectory.\r
+ * @return An integer CVS status for the given sub-directory.\r
+ * @exception CvsException if the CVS process failed.\r
+ */\r
+\r
+ public int getDirectoryStatus(String subdir)\r
+ throws CvsException\r
+ {\r
+ return getDirectoryStatus(subdir, true);\r
+ }\r
+\r
+ /**\r
+ * Get a sub-directory status.\r
+ * @param subdir The name of the subdirectory.\r
+ * @param update If true update all directory entries.\r
+ * @return An integer CVS status for the given sub-directory.\r
+ * @exception CvsException if the CVS process failed.\r
+ */\r
+ public int getDirectoryStatus(String subdir, boolean update) \r
+ throws CvsException\r
+ {\r
+ lru.toHead(this);\r
+ if (update)\r
+ checkDirectoryUse();\r
+ else\r
+ checkDirectoryUse(subdir);\r
+ CvsEntry entry = getDirectoryEntry(subdir);\r
+ return (entry == null) ? DIR_Q : entry.getStatus();\r
+ }\r
+\r
+ /**\r
+ * Check out, or update a sub-directory.\r
+ * @param subdir The sub directory to update.\r
+ * @exception CvsException If the CVS process failed.\r
+ */\r
+\r
+ public void updateDirectory(String subdir) \r
+ throws CvsException\r
+ {\r
+ lru.toHead(this);\r
+ // Check use of the directories:\r
+ checkDirectoryUse();\r
+ // Run the command:\r
+ CvsEntry entry = getDirectoryEntry(subdir);\r
+ if ( entry == null )\r
+ throw new CvsException("Unknown subdirectory "+subdir);\r
+ DirectoryUpdateHandler handler = new DirectoryUpdateHandler(this);\r
+ runner.cvsUpdateDirectory(this, entry.file, handler);\r
+ // Mark the new entry state:\r
+ entry.setStatus(handler.stamp , DIR_CO);\r
+ }\r
+\r
+ /**\r
+ * Get the directory controlled by this manager.\r
+ * @return A File instance locating the directory.\r
+ */\r
+\r
+ public File getDirectory() {\r
+ return directory;\r
+ }\r
+\r
+ /**\r
+ * Create a new CVS manager for the given directory.\r
+ * @param directory The directory for which a manager is to be created.\r
+ * @param props The cvs properties.\r
+ * @param cvspath The absolute path of the cvs program.\r
+ * @param cvsroot The absolute path of the CVS repository. \r
+ * @param cvswrap The absolute path of the cvs wrapper program\r
+ * @exception CvsException If some initialisation failed\r
+ */\r
+\r
+ protected CvsDirectory(File directory\r
+ , Properties props\r
+ , String cvspath\r
+ , String cvsroot\r
+ , String cvswrap[])\r
+ throws CvsException\r
+ {\r
+ if ( ! directory.exists() )\r
+ throw new UncheckedOutException("Unchecked out directory: "\r
+ + directory.getAbsolutePath());\r
+ this.props = props;\r
+ this.cvspath = ((cvspath == null)\r
+ ? props.getProperty(CVSPATH_P, cvspath_def)\r
+ : cvspath);\r
+ this.cvsroot = ((cvsroot == null)\r
+ ? props.getProperty(CVSROOT_P, cvsroot_def)\r
+ : cvsroot);\r
+ this.cvswrapper = ((cvswrap == null) \r
+ ? parseArrayProperty(props.getProperty(\r
+ CVSWRAP_P,\r
+ cvswrap_def))\r
+ : cvswrap);\r
+ this.directory = directory;\r
+ this.cvscheck_stamp = -1;\r
+ this.runner = new CvsRunner();\r
+ }\r
+\r
+ public static void usage() {\r
+ System.out.println("CvsDirectory <dir> [command] [files]");\r
+ System.exit(1);\r
+ }\r
+\r
+ public static void main(String args[]) {\r
+ String command = null;\r
+ String params[] = null;\r
+ File target = null;\r
+\r
+ // Parse command line:\r
+ switch (args.length) {\r
+ case 0:\r
+ usage();\r
+ break;\r
+ case 1:\r
+ target = new File(args[0]);\r
+ break;\r
+ case 2:\r
+ target = new File(args[0]);\r
+ command = args[1];\r
+ break;\r
+ default:\r
+ target = new File(args[0]);\r
+ command = args[1];\r
+ params = new String[args.length-2];\r
+ System.arraycopy(args, 2, params, 0, args.length-2);\r
+ break;\r
+ }\r
+ // Load the directory and perform command:\r
+ try {\r
+ CvsDirectory cvs = CvsDirectory.getManager(new File(args[0]));\r
+ if ( command == null ) {\r
+ cvs.display(System.out);\r
+ } else if ( command.equals("update") ) {\r
+ if ( params != null )\r
+ cvs.update(params);\r
+ else\r
+ cvs.update();\r
+ } else if ( command.equals("status") ) {\r
+ if ( params.length != 1 )\r
+ usage();\r
+ System.out.println(status[cvs.status(params[0])]);\r
+ } else if ( command.equals("diff") ) {\r
+ if ( params.length != 1 )\r
+ usage();\r
+ System.out.println(cvs.diff(params[0]));\r
+ } else if ( command.equals("log") ) {\r
+ if ( params.length != 1 )\r
+ usage();\r
+ System.out.println(cvs.log(params[0]));\r
+ } else if ( command.equals("add") ) {\r
+ if ( params.length == 0 )\r
+ usage();\r
+ cvs.add(params);\r
+ } else if ( command.equals("listdir") ) {\r
+ Enumeration e = cvs.listDirectories();\r
+ while ( e.hasMoreElements() ) {\r
+ String subdir = (String) e.nextElement();\r
+ System.out.println(subdir+\r
+ ": "+\r
+ status[cvs.getDirectoryStatus(subdir)]);\r
+ }\r
+ } else if ( command.equals("updatedir") ) {\r
+ if ( params.length == 0 )\r
+ usage();\r
+ cvs.updateDirectory(params[0]);\r
+ } else {\r
+ System.err.println("Unknown command ["+command+"]");\r
+ }\r
+ } catch (Exception ex) {\r
+ ex.printStackTrace();\r
+ }\r
+ }\r
+\r
+}\r