Adding JMCR-Stable version
[Benchmarks_CSolver.git] / JMCR-Stable / real-world application / jigsaw / src / org / w3c / cvs2 / CvsDirectory.java
diff --git a/JMCR-Stable/real-world application/jigsaw/src/org/w3c/cvs2/CvsDirectory.java b/JMCR-Stable/real-world application/jigsaw/src/org/w3c/cvs2/CvsDirectory.java
new file mode 100644 (file)
index 0000000..7d098db
--- /dev/null
@@ -0,0 +1,1269 @@
+// 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