0001: /*
0002: ** Java cvs client library package.
0003: ** Copyright (c) 1997-2002 by Timothy Gerard Endres
0004: **
0005: ** This program is free software.
0006: **
0007: ** You may redistribute it and/or modify it under the terms of the GNU
0008: ** Library General Public License (LGPL) as published by the Free Software
0009: ** Foundation.
0010: **
0011: ** Version 2 of the license should be included with this distribution in
0012: ** the file LICENSE.txt, as well as License.html. If the license is not
0013: ** included with this distribution, you may find a copy at the FSF web
0014: ** site at 'www.gnu.org' or 'www.fsf.org', or you may write to the Free
0015: ** Software Foundation at 59 Temple Place - Suite 330, Boston, MA 02111 USA.
0016: **
0017: ** THIS SOFTWARE IS PROVIDED AS-IS WITHOUT WARRANTY OF ANY KIND,
0018: ** NOT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY. THE AUTHOR
0019: ** OF THIS SOFTWARE, ASSUMES _NO_ RESPONSIBILITY FOR ANY
0020: ** CONSEQUENCE RESULTING FROM THE USE, MODIFICATION, OR
0021: ** REDISTRIBUTION OF THIS SOFTWARE.
0022: **
0023: */
0024:
0025: package com.ice.cvsc;
0026:
0027: import java.io.*;
0028: import java.lang.*;
0029: import java.text.*;
0030: import java.util.*;
0031: import java.util.zip.*;
0032:
0033: /**
0034: * The CVSProject class implements the concept of a local
0035: * CVS project directory. A local project directory can be
0036: * thought of as a local source code working directory that
0037: * contains a CVS directory containing CVS administration files.
0038: *
0039: * Combined with CVSClient, this class provides everything
0040: * you need to communicate with a CVS Server and maintain
0041: * local working directories for CVS repositories.
0042: *
0043: * @version $Revision: 2.26 $
0044: * @author Timothy Gerard Endres, <time@gjt.org>.
0045: * @see CVSClient
0046: *
0047: */
0048:
0049: //
0050: // NOTES in code:
0051: //
0052: // NB-eol Nick Bower <nick@brainstorm.co.uk>
0053: // Nick Bower's EOL fix. The problem was that our line reader did
0054: // not include the EOL line termination. This made is impossible to
0055: // distinguish between an end-of-file line with no termination, and
0056: // a line that included termination. By explicitly returning the
0057: // EOL (and adjusting for its existence), we eliminate this problem.
0058: // The problem manifested as adding line termination to files that
0059: // lacked it on the last line. Some complained that it affected
0060: // binary files checked in as ascii. That is another issue entirely!
0061: //
0062: // MTT-delete Matthias Tichy <mtt@uni-paderborn.de>
0063: // Code to delete the local file when a "removed" command is received.
0064: //
0065: // MTT-null-list Matthias Tichy <mtt@uni-paderborn.de>
0066: // Fixed to properly handle a null file file.
0067: //
0068: // GG-dot-rep Gérard COLLIN <gcollin@netonomy.com>
0069: // When we see a repository string of ".", it should be ignored.
0070: //
0071:
0072: public class CVSProject extends Object implements CVSResponseHandler {
0073: static public final String RCS_ID = "$Id: CVSProject.java,v 2.26 2003/07/27 01:08:32 time Exp $";
0074: static public final String RCS_REV = "$Revision: 2.26 $";
0075:
0076: static private final String INFO_PREFIX = "# ";
0077: static private final String ERROR_PREFIX = "*** ";
0078: static private final String NOTICE_PREFIX = "==> ";
0079:
0080: static public boolean overTraceRequest = false;
0081: static public boolean overTraceResponse = false;
0082: static public boolean overTraceProcessing = false;
0083: static public boolean overTraceTCP = false;
0084:
0085: static public boolean deepDebug = false;
0086: static public boolean debugEntryIO = false;
0087:
0088: private boolean valid;
0089: private boolean isPServer;
0090: private boolean allowGzipFileMode;
0091: private int gzipStreamLevel;
0092:
0093: private int connMethod;
0094: private int connPort;
0095: private String serverCommand;
0096: private String rshProcess;
0097: private String userName;
0098: private String password;
0099:
0100: private String tempPath;
0101: private String repository;
0102: private String rootDirectory;
0103: private String localRootDirectory;
0104:
0105: private String[] setVars;
0106:
0107: private File localRootDirFile;
0108: private File localAdminDirFile;
0109:
0110: private CVSClient client;
0111: private CVSIgnore ignore;
0112: private CVSProjectDef projectDef;
0113:
0114: private CVSEntry rootEntry;
0115: private Hashtable pathTable;
0116:
0117: /**
0118: * Determines if a pathname, provided by the dirName
0119: * parameter, is a valid CVS administration directory
0120: * (i.e., is a directory named 'CVS').
0121: *
0122: * @param dirName the pathname of the directory in question
0123: */
0124: // UNDONE separator
0125: public static boolean isValidAdminPath(String dirName) {
0126: if (!CVSCUtilities.caseSensitivePathNames()) {
0127: dirName = dirName.toUpperCase();
0128: CVSTracer.traceIf(CVSProject.deepDebug,
0129: "CVSProject.isValidAdminPath:\n"
0130: + " adjusted dirName to '" + dirName
0131: + "'");
0132: }
0133:
0134: return (dirName.endsWith("/CVS") || dirName.endsWith("/CVS/"));
0135: }
0136:
0137: /**
0138: * Given a root path, returns the administration directory
0139: * path corresponding to root's project.
0140: *
0141: * @param dirName the pathname of the root directory
0142: */
0143: // UNDONE separator
0144: public static String rootPathToAdminPath(String dirName) {
0145: return dirName + (dirName.endsWith("/") ? "" : "/") + "CVS";
0146: }
0147:
0148: /**
0149: * Parses a valid CVS Administration Directory path
0150: * and returns the pathname of the working directory
0151: * that the administration directory belongs to. In
0152: * other words, it returns the directory's parent.
0153: *
0154: * @param dirName the pathname of the admin directory
0155: */
0156: // UNDONE separator
0157: public static String adminPathToRootPath(String dirName) {
0158: String path = dirName;
0159:
0160: if (path.endsWith("/")) {
0161: path = path.substring(0, (path.length() - 1));
0162: }
0163:
0164: int index = path.lastIndexOf('/');
0165:
0166: if (index < 0) {
0167: return path;
0168: } else {
0169: return path.substring(0, index);
0170: }
0171: }
0172:
0173: /**
0174: * Parses a valid CVS Entries File pathname and
0175: * returns the pathname of the admin directory
0176: * that the entries files belongs to. In other
0177: * words, it returns the directory's parent.
0178: *
0179: * @param entriesPath The pathname of the Entries file.
0180: */
0181: // UNDONE separator
0182: public static String entriesPathToAdminPath(String entriesPath) {
0183: int index = entriesPath.lastIndexOf('/');
0184:
0185: if (index < 0) {
0186: // UNDONE
0187: return null;
0188: }
0189:
0190: return entriesPath.substring(0, index);
0191: }
0192:
0193: /**
0194: * Verifies that a directory path is a valid CVS
0195: * administration directory. This checks for the
0196: * correct name ('CVS'), and that the necessary
0197: * files ('Entries', 'Root' and 'Repository') are
0198: * present.
0199: *
0200: * @param dirName the pathname of the admin directory
0201: * @return true if directory is valid, otherwise false
0202: */
0203:
0204: public static boolean verifyAdminDirectory(String dirName) {
0205: File file;
0206:
0207: CVSTracer.traceIf(
0208: (CVSProject.deepDebug || CVSProject.debugEntryIO),
0209: "CVSProject.verifyAdminDirectory:\n" + " dirName = '"
0210: + dirName + "'");
0211:
0212: if (!CVSProject.isValidAdminPath(dirName)) {
0213: CVSTracer.traceIf(
0214: (CVSProject.deepDebug || CVSProject.debugEntryIO),
0215: "CVSProject.verifyAdminDirectory:\n"
0216: + " IS NOT a valid admin directory.");
0217: return false;
0218: }
0219:
0220: // NOTE
0221: // Do NOT export until after the verify, as it uses slashes!
0222: //
0223: dirName = CVSCUtilities.exportPath(CVSCUtilities
0224: .stripFinalSlash(dirName));
0225:
0226: file = new File(dirName, "Entries");
0227: if (!file.exists()) {
0228: CVSTracer.traceIf(
0229: (CVSProject.deepDebug || CVSProject.debugEntryIO),
0230: "CVSProject.verifyAdminDirectory:\n"
0231: + " DOES NOT EXIST --> 'Entries'.");
0232: return false;
0233: }
0234:
0235: file = new File(dirName, "Repository");
0236: if (!file.exists()) {
0237: CVSTracer.traceIf(
0238: (CVSProject.deepDebug || CVSProject.debugEntryIO),
0239: "CVSProject.verifyAdminDirectory:\n"
0240: + " DOES NOT EXIST --> 'Repository'.");
0241: return false;
0242: }
0243:
0244: file = new File(dirName, "Root");
0245: if (!file.exists()) {
0246: CVSTracer.traceIf(
0247: (CVSProject.deepDebug || CVSProject.debugEntryIO),
0248: "CVSProject.verifyAdminDirectory:\n"
0249: + " DOES NOT EXIST --> 'Root'.");
0250: return false;
0251: }
0252:
0253: return true;
0254: }
0255:
0256: /**
0257: * Given the administrative directory pathname, return
0258: * the full pathname of the 'Entries' file.
0259: *
0260: * @param adminDirPath The pathname of the admin ('CVS') directory.
0261: */
0262: // UNDONE separator
0263: public static String getAdminEntriesPath(String adminDirPath) {
0264: return (adminDirPath + "/Entries");
0265: }
0266:
0267: /**
0268: * Given the administrative directory pathname, return
0269: * the full pathname of the 'Repository' file.
0270: *
0271: * @param adminDirPath The pathname of the admin ('CVS') directory.
0272: */
0273: // UNDONE separator
0274: public static String getAdminRepositoryPath(String adminDirPath) {
0275: return (adminDirPath + "/Repository");
0276: }
0277:
0278: /**
0279: * Given the administrative directory pathname, return
0280: * the full pathname of the 'Root' file.
0281: *
0282: * @param adminDirPath The pathname of the admin ('CVS') directory.
0283: */
0284: // UNDONE separator
0285: public static String getAdminRootPath(String adminDirPath) {
0286: return (adminDirPath + "/Root");
0287: }
0288:
0289: /**
0290: * Given the administrative directory pathname, return
0291: * the full pathname of the 'Notify' file.
0292: *
0293: * @param adminDirPath The pathname of the admin ('CVS') directory.
0294: */
0295: // UNDONE separator
0296: public static String getAdminNotifyPath(String adminDirPath) {
0297: return (adminDirPath + "/Notify");
0298: }
0299:
0300: /**
0301: * Given the administrative directory pathname, return
0302: * the full pathname of the project preferences file.
0303: *
0304: * @param adminDirPath The pathname of the admin ('CVS') directory.
0305: */
0306: // UNDONE separator
0307: public static String getAdminPrefsPath(String adminDirPath) {
0308: return (adminDirPath + "/jcvs.txt");
0309: }
0310:
0311: /**
0312: * Constructs a new CVSProject object.
0313: */
0314: public CVSProject() {
0315: super ();
0316:
0317: this .initFields();
0318:
0319: this .client = null;
0320: }
0321:
0322: /*
0323: * Constructs a new CVSProject object with the
0324: * provided pro.
0325: *
0326: public
0327: CVSProject( String projectName )
0328: {
0329: super();
0330:
0331: this.initFields();
0332:
0333: this.projectName = projectName;
0334: }
0335: *
0336: */
0337:
0338: /**
0339: * Constructs a new CVSProject object, setting the
0340: * project's client to the one provided.
0341: *
0342: * @param client A CVSClient object to be used by this
0343: * project for all CVS server requests.
0344: */
0345: public CVSProject(CVSClient client) {
0346: super ();
0347:
0348: this .initFields();
0349:
0350: this .client = client;
0351: }
0352:
0353: /**
0354: * Internal nethod used by constructors to initialize
0355: * the project's fields.
0356: */
0357: private void initFields() {
0358: this .valid = false;
0359: this .isPServer = false;
0360: this .allowGzipFileMode = true;
0361: this .gzipStreamLevel = 0;
0362:
0363: this .userName = "";
0364:
0365: // NOTE password == 'null' indicates "no login yet"
0366: this .password = null;
0367:
0368: this .connMethod = CVSRequest.METHOD_RSH;
0369: this .connPort = CVSClient.DEFAULT_CVS_PORT;
0370: this .serverCommand = "cvs server";
0371: this .rshProcess = null;
0372:
0373: this .repository = null;
0374: this .rootDirectory = null;
0375: this .localRootDirectory = null;
0376:
0377: this .client = null;
0378: this .projectDef = null;
0379:
0380: this .setVars = null;
0381:
0382: this .ignore = new CVSIgnore();
0383:
0384: this .rootEntry = null;
0385: this .pathTable = new Hashtable();
0386:
0387: this .tempPath = null;
0388:
0389: this .localRootDirFile = null;
0390: this .localAdminDirFile = null;
0391: }
0392:
0393: /**
0394: * Returns the client this project is set to use.
0395: *
0396: * @return the project's client.
0397: * @see CVSClient
0398: */
0399:
0400: public CVSClient getClient() {
0401: return this .client;
0402: }
0403:
0404: public void setClient(CVSClient client) {
0405: this .client = client;
0406: }
0407:
0408: public String getRepository() {
0409: return this .repository;
0410: }
0411:
0412: public void setRepository(String repository) {
0413: this .repository = repository;
0414: }
0415:
0416: public boolean isPServer() {
0417: return this .isPServer;
0418: }
0419:
0420: public void setPServer(boolean isPServer) {
0421: this .isPServer = isPServer;
0422: }
0423:
0424: public boolean allowsGzipFileMode() {
0425: return this .allowGzipFileMode;
0426: }
0427:
0428: public void setAllowsGzipFileMode(boolean allow) {
0429: this .allowGzipFileMode = allow;
0430: }
0431:
0432: public int getGzipStreamLevel() {
0433: return this .gzipStreamLevel;
0434: }
0435:
0436: public void setGzipStreamLevel(int level) {
0437: this .gzipStreamLevel = level;
0438: }
0439:
0440: public String getUserName() {
0441: return this .userName;
0442: }
0443:
0444: public void setUserName(String name) {
0445: this .userName = name;
0446: }
0447:
0448: public String getPassword() {
0449: return this .password;
0450: }
0451:
0452: public void setPassword(String password) {
0453: this .password = password;
0454: }
0455:
0456: public String getRootDirectory() {
0457: return this .rootDirectory;
0458: }
0459:
0460: public void setRootDirectory(String rootDirectory) {
0461: this .rootDirectory = rootDirectory;
0462: }
0463:
0464: /**
0465: * Returns the <em>full</em> local pathname for the
0466: * root directory of this project.
0467: *
0468: * @return Full pathname of project's local root directory.
0469: */
0470:
0471: public String getLocalRootPath() {
0472: return this .localRootDirectory;
0473: // + "/" + this.rootEntry.getName();
0474: }
0475:
0476: public String getLocalRootDirectory() {
0477: return this .localRootDirectory;
0478: }
0479:
0480: public void setLocalRootDirectory(String dirName) {
0481: this .localRootDirectory = dirName;
0482:
0483: this .localRootDirFile = new File(dirName);
0484:
0485: this .localAdminDirFile = // UNDONE separator
0486: new File(dirName + "/CVS");
0487: }
0488:
0489: public String getTempDirectory() {
0490: return this .tempPath;
0491: }
0492:
0493: public void setTempDirectory(String dirName) {
0494: this .tempPath = dirName;
0495: if (this .client != null) {
0496: this .client.setTempDirectory(dirName);
0497: }
0498: }
0499:
0500: public int getConnectionPort() {
0501: return this .connPort;
0502: }
0503:
0504: public void setConnectionPort(int port) {
0505: this .connPort = port;
0506: }
0507:
0508: public int getConnectionMethod() {
0509: return this .connMethod;
0510: }
0511:
0512: public void setConnectionMethod(int method) {
0513: this .connMethod = method;
0514: }
0515:
0516: public boolean isSSHServer() {
0517: return (this .connMethod == CVSRequest.METHOD_SSH);
0518: }
0519:
0520: public String getServerCommand() {
0521: return this .serverCommand;
0522: }
0523:
0524: public void setServerCommand(String command) {
0525: this .serverCommand = command;
0526: }
0527:
0528: public String getRshProcess() {
0529: return this .rshProcess;
0530: }
0531:
0532: public void setRshProcess(String rshProcess) {
0533: this .rshProcess = rshProcess;
0534: }
0535:
0536: /**
0537: * Returns the project's user set variables.
0538: *
0539: * @return The project's user set variables.
0540: */
0541: public String[] getSetVariables() {
0542: return this .setVars;
0543: }
0544:
0545: /**
0546: * Sets the project's user set variables.
0547: *
0548: * @param vars The new user set variables.
0549: */
0550: public void setSetVariables(String[] vars) {
0551: this .setVars = vars;
0552: }
0553:
0554: public CVSEntry getRootEntry() {
0555: return this .rootEntry;
0556: }
0557:
0558: public CVSProjectDef getProjectDef() {
0559: return this .projectDef;
0560: }
0561:
0562: public void setProjectDef(CVSProjectDef projectDef) {
0563: this .projectDef = projectDef;
0564: }
0565:
0566: public File getEntryFile(CVSEntry entry) {
0567: String relPath;
0568:
0569: relPath = entry.getFullName();
0570:
0571: File file = new File(CVSCUtilities
0572: .exportPath(this .localRootDirFile.getPath()),
0573: CVSCUtilities.exportPath(entry.getFullPathName()));
0574:
0575: if (CVSProject.deepDebug)
0576: CVSTracer.traceIf(false,
0577: "CVSProject.getEntryFile: relPath '" + relPath
0578: + "' localRootDir '"
0579: + this .localRootDirFile.getPath()
0580: + "' result '" + file.getPath() + "'");
0581:
0582: return file;
0583: }
0584:
0585: public boolean hasValidLogin(String userName) {
0586: if (this .userName.equals(userName))
0587: if (this .password != null)
0588: return true;
0589:
0590: return false;
0591: }
0592:
0593: public void addEntryNotify(CVSEntryVector entries, String type,
0594: String options) {
0595: PrintWriter out;
0596: String noteLine;
0597:
0598: // REVIEW
0599: // UNDONE
0600: // We are INCOMPATIBLE with the cvs command line here!!!
0601: // The command line stores this file in each working directory's
0602: // admin directory, NOT in the root admin like we are! This means
0603: // that the command line will get all of our notifies, ( will it
0604: // choke on them?), but we will not get all of the command line's.
0605: // Why?! Performance. Does it matter?
0606: //
0607: String fileName = CVSProject.getAdminNotifyPath(CVSProject
0608: .rootPathToAdminPath(this .getLocalRootPath()));
0609:
0610: try {
0611: out = new PrintWriter(new FileWriter(fileName, true));
0612: } catch (IOException ex) {
0613: CVSTracer
0614: .traceWithStack("ERROR opening Notification file '"
0615: + fileName + "' for append");
0616: return;
0617: }
0618:
0619: CVSTimestamp now = new CVSTimestamp();
0620: CVSTimestampFormat stamper = CVSTimestampFormat.getInstance();
0621:
0622: String stampStr = stamper.format(now);
0623:
0624: for (int eIdx = 0; entries != null && eIdx < entries.size(); ++eIdx) {
0625: CVSEntry entry = entries.entryAt(eIdx);
0626: if (entry != null) {
0627: out.println(type + entry.getName() + "\t"
0628: + stamper.format(now) + " GMT" + "\t"
0629: + "remote.via.jCVS" + "\t"
0630: + entry.getLocalDirectory() + "\t" + options);
0631: } else {
0632: CVSTracer.traceWithStack("NULL ENTRY[" + eIdx
0633: + "] on index '" + eIdx + "'");
0634: }
0635: }
0636:
0637: out.flush();
0638: out.close();
0639: }
0640:
0641: public void includeNotifies(CVSRequest request) {
0642: BufferedReader in;
0643: String noteLine;
0644:
0645: request.notifies = new Vector();
0646:
0647: if (this .rootEntry == null)
0648: return;
0649:
0650: File notFile = new File(CVSProject
0651: .getAdminNotifyPath(CVSProject.rootPathToAdminPath(this
0652: .getLocalRootPath())));
0653:
0654: if (notFile.exists()) {
0655: try {
0656: in = new BufferedReader(new FileReader(notFile));
0657: } catch (IOException ex) {
0658: CVSLog.logMsg("ERROR opening Notification file '"
0659: + notFile.getPath() + "'");
0660: return;
0661: }
0662:
0663: for (;;) {
0664: try {
0665: noteLine = in.readLine();
0666: } catch (IOException ex) {
0667: CVSLog.logMsg("ERROR reading Notification file '"
0668: + notFile.getPath() + "'");
0669: noteLine = null;
0670: }
0671:
0672: if (noteLine == null)
0673: break;
0674:
0675: // NB-eol
0676: // Now need this because NewLineReader.readLine
0677: // returns an eol.
0678: noteLine = noteLine.trim();
0679:
0680: CVSNotifyItem notifyItem = parseNotifyLine(noteLine);
0681:
0682: if (notifyItem != null) {
0683: request.notifies.addElement(notifyItem);
0684: } else {
0685: CVSLog.logMsg("ERROR bad 'CVS/Notify' line:\n"
0686: + " " + noteLine);
0687: }
0688: }
0689:
0690: try {
0691: in.close();
0692: } catch (IOException ex) {
0693: }
0694: }
0695: }
0696:
0697: public boolean verifyPassword(CVSUserInterface ui, String userName,
0698: String password, boolean trace) {
0699: CVSRequest request;
0700: boolean result = false;
0701:
0702: if (!this .isPServer() && !this .isSSHServer())
0703: return true;
0704:
0705: if (this .hasValidLogin(userName))
0706: return true;
0707:
0708: String scrambled = CVSScramble.scramblePassword(password, 'A');
0709:
0710: request = new CVSRequest();
0711:
0712: request.setPServer(this .isPServer());
0713: request.setUserName(userName);
0714: request.setPassword(this .isSSHServer() ? password : scrambled);
0715:
0716: request.setPort(this .getClient().getPort());
0717: request.setHostName(this .getClient().getHostName());
0718:
0719: request.setRepository(this .repository);
0720: request.setRootDirectory(this .rootDirectory);
0721: request.setLocalDirectory(this .localRootDirectory);
0722:
0723: request.verificationOnly = true;
0724:
0725: request.traceRequest = trace;
0726: request.traceResponse = trace;
0727: request.traceProcessing = trace;
0728: request.traceTCPData = trace;
0729: request.allowGzipFileMode = this .allowGzipFileMode;
0730: request.gzipStreamLevel = this .gzipStreamLevel;
0731:
0732: request.setConnectionMethod(this .getConnectionMethod());
0733: request.setServerCommand(this .getServerCommand());
0734: request.setRshProcess(this .getRshProcess());
0735:
0736: request.setUserInterface(ui);
0737:
0738: CVSResponse response = client.processCVSRequest(request);
0739:
0740: if (response.getStatus() == CVSResponse.OK) {
0741: result = true;
0742: this .setUserName(userName);
0743: this .setPassword(this .isSSHServer() ? password : scrambled);
0744: response.appendStdout("Authentication of '" + userName
0745: + "' succeeded.\n");
0746: } else {
0747: result = false;
0748: this .password = null;
0749: response.appendStdout("Authentication of '" + userName
0750: + "' failed.\n");
0751: }
0752:
0753: if (ui != null && response != null)
0754: ui.uiDisplayResponse(response);
0755:
0756: if (response != null && !request.saveTempFiles)
0757: response.deleteTempFiles();
0758:
0759: return result;
0760: }
0761:
0762: /**
0763: * Given a repository path, which was not found in the
0764: * pathTable, determine if the path is in the table if
0765: * case is ignored. This is to support platforms which
0766: * have case insensitive path names.
0767: *
0768: * @param subPath The path to check for in the table.
0769: * @return The CVSEntry representing the path's directory.
0770: */
0771: CVSEntry getPathIgnoringCase(String subPath) {
0772: Enumeration numer = this .pathTable.keys();
0773:
0774: for (; numer.hasMoreElements();) {
0775: String key = (String) numer.nextElement();
0776:
0777: if (key.equalsIgnoreCase(subPath)) {
0778: return (CVSEntry) this .pathTable.get(key);
0779: }
0780: }
0781:
0782: return null;
0783: }
0784:
0785: /**
0786: * Given a 'local directory' (in the protocol sense), get the
0787: * corresponding directory CVSEntry. This method will return
0788: * null if the directory hierarchy has not been "ensured" yet.
0789: *
0790: * @param localDir The directory's 'local directory' name.
0791: */
0792:
0793: public CVSEntry getDirEntryForLocalDir(String localDir) {
0794: return this .getPathTableEntry(localDir);
0795: }
0796:
0797: private CVSEntry getPathTableEntry(String path) {
0798: CVSEntry result = null;
0799:
0800: result = (CVSEntry) this .pathTable.get(path);
0801:
0802: if (result == null && !CVSCUtilities.caseSensitivePathNames()) {
0803: result = this .getPathIgnoringCase(path);
0804: if (CVSProject.deepDebug)
0805: CVSTracer.traceIf(true,
0806: "getPathTableEntry: CASE INsensitive TABLE CHECK\n"
0807: + " result '"
0808: + (result == null ? "(null)" : result
0809: .getName())
0810: + "'\n"
0811: + " reposirory '"
0812: + (result != null ? result
0813: .getRepository() : "null")
0814: + "'");
0815: }
0816:
0817: return result;
0818: }
0819:
0820: private CVSEntry reversePathTableEntry(String repository) {
0821: CVSEntry result = null;
0822:
0823: Enumeration numer = this .pathTable.keys();
0824: for (boolean match = false; !match && numer.hasMoreElements();) {
0825: String localDir = (String) numer.nextElement();
0826: CVSEntry tblEntry = (CVSEntry) this .pathTable.get(localDir);
0827:
0828: if (CVSCUtilities.caseSensitivePathNames())
0829: match = repository.equals(tblEntry.getRepository());
0830: else
0831: match = repository.equalsIgnoreCase(tblEntry
0832: .getRepository());
0833:
0834: if (match)
0835: result = tblEntry;
0836: }
0837:
0838: if (CVSProject.deepDebug)
0839: CVSTracer.traceIf(true,
0840: "CVSProject.reversePathTableEntry:\n"
0841: + " repository = '"
0842: + repository
0843: + "'\n"
0844: + " RESULT =\n"
0845: + (result == null ? "(null)" : result
0846: .dumpString(" ")));
0847:
0848: return result;
0849: }
0850:
0851: /**
0852: * Guarentees that the repository contains the path specified.
0853: * This will in turn invoke server commands to create the
0854: * directories needed to make the path exist, so this can and
0855: * will change the repository on the server. The repositoryPath
0856: * is relative to the repository's root directory.
0857: *
0858: * @param ui The CVS User Interface to display the progress.
0859: * @param localDirectory The <em>relative</em> path to ensure.
0860: * @return A CVSResponse with the results of each directory 'add'.
0861: */
0862: public CVSResponse ensureRepositoryPath(CVSUserInterface ui,
0863: String localDirectory, CVSResponse resultResp) {
0864: int index;
0865: CVSEntry dirEntry;
0866: CVSRequest request;
0867: boolean result;
0868:
0869: CVSTracer.traceIf(CVSProject.deepDebug,
0870: "CVSProject.ensureRepositoryPath: \n"
0871: + " localDirectory '" + localDirectory + "'");
0872:
0873: CVSEntryVector entries = new CVSEntryVector();
0874:
0875: // Since we will be re-using this vector possibly many times,
0876: // we can't keep appending. Thus, we append once here to fill
0877: // in slots zero and one, then use setEntry() below
0878: //
0879: entries.appendEntry(null);
0880: entries.appendEntry(null);
0881:
0882: CVSTracer.traceIf(CVSProject.deepDebug,
0883: "ensureRepositoryPath: ROOT =\n "
0884: + this .rootEntry.dumpString());
0885:
0886: // The root directory has to exist by this point.
0887: String repository = this .rootEntry.getRepository();
0888: CVSTracer.traceIf(CVSProject.deepDebug,
0889: "ensureRepositoryPath: rootEntry repository = '"
0890: + repository + "'");
0891:
0892: resultResp.setStatus(CVSResponse.OK);
0893:
0894: CVSEntry parentEntry = this .rootEntry;
0895:
0896: for (int offset = 2;;) {
0897: index = localDirectory.indexOf('/', offset);
0898:
0899: CVSTracer.traceIf(CVSProject.deepDebug,
0900: "ensureRepositoryPath: indexOf( '/'," + offset
0901: + " ) = " + index);
0902:
0903: if (index < 0) {
0904: CVSTracer.traceIf(CVSProject.deepDebug,
0905: "ensureRepositoryPath: DONE w/ REMAINDER '"
0906: + localDirectory.substring(offset)
0907: + "'");
0908: break;
0909: }
0910:
0911: offset = index + 1;
0912: String localDir = localDirectory.substring(0, index + 1);
0913: dirEntry = this .getPathTableEntry(localDir);
0914:
0915: CVSTracer.traceIf(CVSProject.deepDebug,
0916: "ensureRepositoryPath: localDir '"
0917: + localDir
0918: + "' returns "
0919: + (dirEntry == null ? "null" : dirEntry
0920: .dumpString()));
0921:
0922: if (dirEntry != null) {
0923: if (CVSProject.deepDebug)
0924: CVSTracer.traceIf(true,
0925: "ensureRepositoryPath: EXISTING DIRECTORY '"
0926: + localDir + "'\n"
0927: + " localDir '"
0928: + dirEntry.getLocalDirectory()
0929: + "'\n" + " repository '"
0930: + dirEntry.getRepository() + "'");
0931: parentEntry = dirEntry;
0932: continue;
0933: }
0934:
0935: CVSTracer.traceIf(CVSProject.deepDebug,
0936: "ensureRepositoryPath: NEW CVS DIRECTORY '"
0937: + localDir + "'\n"
0938: + " Parent LocalDirectory '"
0939: + parentEntry.getLocalDirectory() + "'\n"
0940: + " Parent Repository '"
0941: + parentEntry.getRepository() + "'");
0942:
0943: request = new CVSRequest();
0944:
0945: String name = localDir.substring(0, localDir.length() - 1);
0946: index = name.lastIndexOf('/');
0947: if (index >= 0 && index < (name.length() - 1)) {
0948: name = name.substring(index + 1);
0949: }
0950:
0951: String rootDir = CVSCUtilities.ensureFinalSlash(this
0952: .getRootDirectory());
0953:
0954: dirEntry = new CVSEntry();
0955: dirEntry.setName(name);
0956: dirEntry.setLocalDirectory(localDir);
0957: dirEntry.setRepository(parentEntry.getRepository() + "/"
0958: + name);
0959: // We need this next line to mark dirEntry as a directory!!
0960: dirEntry.setDirectoryEntryList(new CVSEntryVector());
0961:
0962: entries.setElementAt(parentEntry, 0);
0963: entries.setElementAt(dirEntry, 1);
0964: request.setEntries(entries);
0965:
0966: CVSTracer.traceIf(CVSProject.deepDebug,
0967: "ensureRepositoryPath: DIR ENTRY\n"
0968: + " Name " + dirEntry.getName()
0969: + "\n" + " LocalDir "
0970: + dirEntry.getLocalDirectory() + "\n"
0971: + " Repository "
0972: + dirEntry.getRepository());
0973:
0974: CVSTracer.traceIf(CVSProject.deepDebug,
0975: "ensureRepositoryPath: PARENT ENTRY\n"
0976: + " Name " + parentEntry.getName()
0977: + "\n" + " LocalDir "
0978: + parentEntry.getLocalDirectory() + "\n"
0979: + " Reposirory "
0980: + parentEntry.getRepository());
0981:
0982: request.execInCurDir = true;
0983: request.setDirEntry(parentEntry);
0984:
0985: request.sendEntries = true;
0986: request.sendArguments = true;
0987: request.sendEntryFiles = false;
0988:
0989: request.traceRequest = true; // CVSProject.overTraceRequest;
0990: request.traceResponse = true; // CVSProject.overTraceResponse;
0991: request.traceTCPData = true; // CVSProject.overTraceTCP;
0992: request.traceProcessing = true; // CVSProject.overTraceProcessing;
0993:
0994: request.allowGzipFileMode = this .allowGzipFileMode;
0995: request.gzipStreamLevel = this .gzipStreamLevel;
0996:
0997: request.setUserName(this .userName);
0998: request.setPServer(this .isPServer());
0999:
1000: // NOTE this.password is "correct" (scrambled for PServer
1001: // mode, and not scrambled for SSH mode).
1002: if (this .isPServer() || this .isSSHServer()) {
1003: request.setPassword(this .password);
1004: }
1005:
1006: request.setPort(this .getClient().getPort());
1007: request.setHostName(this .getClient().getHostName());
1008: request.setRshProcess(this .getRshProcess());
1009: request.setPort(this .getConnectionPort());
1010: request.setConnectionMethod(this .getConnectionMethod());
1011:
1012: request.setRepository(this .repository);
1013: request.setRootRepository(this .rootEntry.getRepository());
1014: request.setRootDirectory(this .rootDirectory);
1015: request.setLocalDirectory(this .localRootDirectory);
1016:
1017: request.setServerCommand(this .getServerCommand());
1018: request.setSetVariables(this .setVars);
1019: this .establishNewDirSticky(request, dirEntry);
1020: this .establishStickys(request);
1021: this .establishStatics(request);
1022:
1023: request.setCommand("add");
1024:
1025: request
1026: .setUserInterface((ui == null) ? (CVSUserInterface) this
1027: : ui);
1028: request.includeNotifies = false;
1029: request.queueResponse = true;
1030:
1031: CVSArgumentVector arguments = new CVSArgumentVector();
1032: arguments.appendArgument(name);
1033: request.setArguments(arguments);
1034:
1035: CVSResponse response = client.processCVSRequest(request);
1036:
1037: response.deleteTempFiles(); // There shouldn't be any...
1038:
1039: String err = response.getStderr();
1040: if (err != null && err.length() > 0)
1041: resultResp.appendStderr(err);
1042:
1043: String out = response.getStdout();
1044: if (out != null && out.length() > 0)
1045: resultResp.appendStdout(out);
1046:
1047: if (response.getStatus() == CVSResponse.OK) {
1048: CVSTracer.traceIf(CVSProject.deepDebug,
1049: "ensureRepositoryPath: ensureEntryHierarchy( "
1050: + dirEntry.getLocalDirectory() + ", "
1051: + dirEntry.getRepository() + " )");
1052:
1053: this .ensureEntryHierarchy(dirEntry.getLocalDirectory(),
1054: dirEntry.getRepository());
1055:
1056: dirEntry = this .getPathTableEntry(localDir);
1057: if (dirEntry == null) {
1058: CVSTracer
1059: .traceWithStack("WHAT?! ensured, but no pathTable entry '"
1060: + localDir + "'?!?!");
1061: } else {
1062: dirEntry.setDirty(true);
1063: }
1064:
1065: this .ensureProperWorkingDirectory(
1066: this .localRootDirectory, localDir, true);
1067: } else {
1068: resultResp.setStatus(CVSResponse.ERROR);
1069: resultResp.appendStdOut(response.getStdout());
1070: resultResp.appendStdErr(response.getStderr());
1071:
1072: if (request.getUserInterface() != null)
1073: request.getUserInterface().uiDisplayResponse(
1074: resultResp);
1075:
1076: CVSTracer.traceIf(true,
1077: "ensureRepositoryPath: ERROR! SERVER RESPONSE:\n"
1078: + response.getStderr() + "\n"
1079: + response.getStdout());
1080:
1081: break;
1082: }
1083:
1084: repository = repository + "/" + name;
1085: }
1086:
1087: return resultResp;
1088: }
1089:
1090: private String getStickyTagspec(CVSEntry entry) {
1091: String result = "";
1092:
1093: String rootPath = CVSProject.rootPathToAdminPath(this
1094: .getLocalRootDirectory()
1095: + "/" + entry.getLocalPathName());
1096:
1097: File stickyFile = new File(rootPath, "Tag");
1098: if (stickyFile.exists()) {
1099: try {
1100: result = CVSCUtilities.readStringFile(stickyFile);
1101: } catch (IOException ex) {
1102: result = "";
1103: }
1104:
1105: if (!(result.startsWith("D") || result.startsWith("T")
1106: // REVIEW - Where does N enter the picture?!
1107: // appears to be on "cvs add dir".
1108: || result.startsWith("N"))) {
1109: result = "";
1110: }
1111: }
1112:
1113: return result;
1114: }
1115:
1116: /**
1117: * This methods deal with adding a new directory which is not
1118: * yet in the repository, and yet, whose parent directory has
1119: * a sticky tag set. We wish to "inherit" that tag...
1120: */
1121:
1122: public void establishNewDirSticky(CVSRequest request, CVSEntry entry) {
1123: Hashtable stickys = request.getStickys();
1124: if (stickys == null)
1125: stickys = new Hashtable();
1126:
1127: String localDir = entry.getLocalDirectory();
1128: String parentDir = CVSCUtilities.getLocalParent(localDir);
1129:
1130: String rootPath = CVSProject.rootPathToAdminPath(this
1131: .getLocalRootDirectory()
1132: + "/" + parentDir);
1133:
1134: String tagSpec = "";
1135: File stickyFile = new File(rootPath, "Tag");
1136: if (stickyFile.exists()) {
1137: try {
1138: tagSpec = CVSCUtilities.readStringFile(stickyFile);
1139: } catch (IOException ex) {
1140: ex.printStackTrace();
1141: }
1142: }
1143:
1144: if (tagSpec.length() > 0) {
1145: rootPath = CVSProject.rootPathToAdminPath(this
1146: .getLocalRootDirectory()
1147: + "/" + localDir);
1148:
1149: File adminDir = new File(rootPath);
1150: adminDir.mkdirs();
1151: stickyFile = new File(rootPath, "Tag");
1152: try {
1153: if (!stickyFile.exists()) {
1154: CVSCUtilities.writeStringFile(stickyFile, tagSpec);
1155: }
1156: } catch (IOException ex) {
1157: ex.printStackTrace();
1158: }
1159: }
1160:
1161: stickys.put(localDir, tagSpec);
1162: stickys.put(parentDir, tagSpec);
1163: request.setStickys(stickys);
1164: }
1165:
1166: public void establishStickys(CVSRequest request) {
1167: Hashtable stickys = new Hashtable();
1168:
1169: CVSEntryVector entries = request.getEntries();
1170: for (int i = 0, sz = entries.size(); i < sz; ++i) {
1171: CVSEntry entry = (CVSEntry) entries.elementAt(i);
1172: String localDir = entry.getLocalDirectory();
1173: if (stickys.get(localDir) == null) {
1174: String tagSpec = this .getStickyTagspec(entry);
1175: stickys.put(localDir, tagSpec);
1176: }
1177: }
1178:
1179: if (stickys.size() > 0)
1180: request.setStickys(stickys);
1181: }
1182:
1183: private boolean isStaticDirectory(CVSEntry entry) {
1184: String rootPath = CVSProject.rootPathToAdminPath(this
1185: .getLocalRootDirectory()
1186: + "/" + entry.getLocalPathName());
1187:
1188: File staticFile = new File(rootPath, "Entries.static");
1189:
1190: return staticFile.exists();
1191: }
1192:
1193: public void establishStatics(CVSRequest request) {
1194: Hashtable statics = new Hashtable();
1195:
1196: CVSEntryVector entries = request.getEntries();
1197: for (int i = 0, sz = entries.size(); i < sz; ++i) {
1198: CVSEntry entry = (CVSEntry) entries.elementAt(i);
1199: String localDir = entry.getLocalDirectory();
1200: if (statics.get(localDir) == null) {
1201: if (this .isStaticDirectory(entry))
1202: statics.put(localDir, "");
1203: }
1204: }
1205:
1206: if (statics.size() > 0)
1207: request.setStatics(statics);
1208: }
1209:
1210: public boolean performCVSRequest(CVSRequest request) {
1211: return this .performCVSRequest(request, new CVSResponse());
1212: }
1213:
1214: public boolean performCVSRequest(CVSRequest request,
1215: CVSResponse response) {
1216: boolean result = true;
1217:
1218: request.setUserName(this .userName);
1219: request.setPServer(this .isPServer());
1220:
1221: if (this .isPServer() || this .isSSHServer()) {
1222: request.setPassword(this .password);
1223: }
1224:
1225: String rootRepository;
1226: if (this .rootEntry != null) {
1227: rootRepository = this .rootEntry.getRepository();
1228:
1229: //
1230: // SPECIAL CASE
1231: // When we are asked to send the "module name" as an argment,
1232: // we have a problem. The module name "must" always be "."
1233: // (see CVSClient.c) to be correct. However, if the rootRepository
1234: // is sent from the root, then "." will be wrong. Thus, we will
1235: // override it here to make it right for this one case.
1236: //
1237: // NOTE Release 5.0.8: I do not know the source of this original
1238: // "fix", but it breaks the "Update" module command (nothing
1239: // happens at the top level). Removing this did not appear
1240: // to break anything. So far...
1241: //
1242: /*
1243: if ( request.sendModule )
1244: {
1245: CVSEntry repEnt = this.rootEntry.getEntryList().getEntryAt(0);
1246: CVSTracer.traceIf( this.deepDebug,
1247: "CVSProject.performCVSRequest: APPLY MODULE NAME HACK\n"
1248: + " repEnt =\n"
1249: + (repEnt==null?"NULL":repEnt.dumpString( " " )) );
1250:
1251: if ( repEnt != null )
1252: {
1253: rootRepository = repEnt.getRepository();
1254: }
1255: }
1256: */
1257: } else {
1258: // This is 'checkout' or 'export' case.
1259: if (this .repository.equals("."))
1260: rootRepository = this .rootDirectory;
1261: else
1262: rootRepository = this .rootDirectory + "/"
1263: + this .repository;
1264: }
1265:
1266: request.setHostName(this .getClient().getHostName());
1267: request.setRepository(this .repository);
1268: request.setRootRepository(rootRepository);
1269: request.setRootDirectory(this .rootDirectory);
1270: request.setLocalDirectory(this .localRootDirectory);
1271: request.setPort(this .getConnectionPort());
1272: request.setConnectionMethod(this .getConnectionMethod());
1273: request.setServerCommand(this .getServerCommand());
1274: request.setRshProcess(this .getRshProcess());
1275:
1276: request.setSetVariables(this .setVars);
1277:
1278: this .establishStickys(request);
1279:
1280: this .establishStatics(request);
1281:
1282: if (request.includeNotifies) {
1283: this .includeNotifies(request);
1284: }
1285:
1286: if (!request.queueResponse)
1287: if (request.responseHandler == null)
1288: request.responseHandler = this ;
1289:
1290: if (CVSProject.overTraceRequest)
1291: request.traceRequest = CVSProject.overTraceRequest;
1292: if (CVSProject.overTraceResponse)
1293: request.traceResponse = CVSProject.overTraceResponse;
1294: if (CVSProject.overTraceProcessing)
1295: request.traceProcessing = CVSProject.overTraceProcessing;
1296: if (CVSProject.overTraceTCP)
1297: request.traceTCPData = CVSProject.overTraceTCP;
1298:
1299: request.allowGzipFileMode = this .allowGzipFileMode;
1300: request.gzipStreamLevel = this .gzipStreamLevel;
1301:
1302: if (!request.verifyRequest()) {
1303: CVSLog
1304: .logMsg("CVSProject.performCVSRequest: BAD CVSRequest: '"
1305: + request.getVerifyFailReason() + "'");
1306: return false;
1307: } else {
1308: this .client.processCVSRequest(request, response);
1309:
1310: this .processCVSResponse(request, response);
1311:
1312: if (request.getCommand().equals("update")
1313: && (request.getArguments().containsArgument("-P")
1314: || request.getArguments().containsArgument(
1315: "-r") || request.getArguments()
1316: .containsArgument("-D"))) {
1317: this .pruneEmptySubDirs(request.handleEntries);
1318: }
1319:
1320: if (request.getUserInterface() != null && response != null)
1321: request.getUserInterface().uiDisplayResponse(response);
1322:
1323: if (response != null && !request.saveTempFiles)
1324: response.deleteTempFiles();
1325:
1326: return (response.getStatus() == CVSResponse.OK);
1327: }
1328: }
1329:
1330: public CVSEntry entryLineToEntry(String entryLine) {
1331: CVSEntry entry = new CVSEntry();
1332:
1333: try {
1334: entry.parseEntryLine(entryLine, true);
1335: } catch (ParseException ex) {
1336: entry = null;
1337: CVSLog.traceMsg(ex, "CVSProject.entryFromEntryLine: ERROR "
1338: + "could not process entry line '" + entryLine);
1339: }
1340:
1341: return entry;
1342: }
1343:
1344: public File getLocalEntryFile(CVSEntry entry) {
1345: File result = new File(CVSCUtilities
1346: .exportPath(this .localRootDirectory), CVSCUtilities
1347: .exportPath(entry.getFullPathName()));
1348:
1349: return result;
1350: }
1351:
1352: /**
1353: * Given a local-directory returned from the server,
1354: * make sure the local-directory is in a format that
1355: * jCVS can make use of (i.e., via the pathTable).
1356: * Currently, the only case handled is when local-directory
1357: * is './', which forces us to locate from pathTable.
1358: *
1359: * @param pathName The local-directory from the server.
1360: * @param repository The repository the server sent with this local-directory.
1361: * @return The normalized local-directory, or null if it does not exist.
1362: */
1363:
1364: public String normalizeLocalDirectory(String pathName,
1365: String repository) {
1366: String result = pathName;
1367:
1368: if (pathName.equals("./")) {
1369: CVSTracer.traceIf(this .deepDebug,
1370: "normalizeLocalDirectory: SPECIAL './' CASE.\n"
1371: + " pathName '" + pathName + "'\n"
1372: + " repository '" + repository + "'");
1373:
1374: // SPECIAL CASE
1375: // Here, we have a case where a command executed
1376: // in a subdirectory (or root), and instead of the
1377: // usual root-based local-directory, we get this.
1378: // We need to take the repository and reverse lookup
1379: // the local-directory.
1380:
1381: CVSEntry revEntry = this .reversePathTableEntry(repository);
1382:
1383: if (revEntry != null) {
1384: result = revEntry.getLocalDirectory();
1385: } else {
1386: result = null;
1387: CVSTracer.traceIf(true, "COULD NOT RESOLVE '"
1388: + pathName + "' with '" + repository + "'");
1389: CVSTracer.traceWithStack("COULD NOT RESOLVE '"
1390: + pathName + "' with '" + repository + "'");
1391: }
1392: }
1393:
1394: CVSTracer.traceIf(this .deepDebug,
1395: "normalizeLocalDirectory: RESULT '" + pathName
1396: + "' ---> '" + result + "'");
1397:
1398: return result;
1399: }
1400:
1401: public CVSEntry createItemEntry(CVSResponseItem item) {
1402: CVSEntry entry;
1403: String entryLine = item.getEntriesLine();
1404:
1405: CVSTracer.traceIf(this .deepDebug, "createItemEntry:\n"
1406: + " item.getPathName '" + item.getPathName()
1407: + "'\n" + " item.repositoryName '"
1408: + item.getRepositoryName() + "'\n"
1409: + " item.getEntriesLine '" + item.getEntriesLine()
1410: + "'");
1411:
1412: // NOTE
1413: // When the entryLine is null, all we are interested
1414: // in is the name, localDirectory, and repository...
1415: //
1416: if (entryLine == null)
1417: entry = new CVSEntry();
1418: else
1419: entry = this .entryLineToEntry(item.getEntriesLine());
1420:
1421: if (entry != null) {
1422: String repos = item.getRepositoryName();
1423: int index = repos.lastIndexOf('/');
1424:
1425: if (index < 0) {
1426: CVSTracer
1427: .traceWithStack("CVSProject.createItemEntry: ERROR "
1428: + "repository '"
1429: + repos
1430: + "' has no slash!");
1431: entry.setName(repos);
1432: entry.setRepository("");
1433: } else {
1434: entry.setName(repos.substring(index + 1));
1435: entry.setRepository(repos.substring(0, index));
1436: }
1437:
1438: String localDir = this .normalizeLocalDirectory(item
1439: .getPathName(), entry.getRepository());
1440:
1441: entry.setLocalDirectory(localDir);
1442: }
1443:
1444: return entry;
1445: }
1446:
1447: public boolean handleResponseItem(CVSRequest request,
1448: CVSResponse response, CVSResponseItem item) {
1449: boolean result;
1450:
1451: CVSTracer
1452: .traceIf(request.traceProcessing,
1453: "CVSProject.handleResponseItem:\n "
1454: + item.toString());
1455:
1456: result = this .processResponseItem(request, response, item);
1457:
1458: if (!request.saveTempFiles) {
1459: item.deleteFile();
1460: }
1461:
1462: return result;
1463: }
1464:
1465: public boolean processCVSResponse(CVSRequest request,
1466: CVSResponse response) {
1467: int idx;
1468: boolean ok;
1469: CVSEntry entry = null;
1470: boolean result = true;
1471: File localFile = null;
1472: CVSResponseItem item = null;
1473:
1474: if (response == null)
1475: return true;
1476:
1477: // NOTE
1478: // We process the item list, EVEN when !queueResponse,
1479: // since the responseHandler may have queued some of the
1480: // response items for processing here!!!
1481: //
1482: CVSRespItemVector items = response.getItemList();
1483:
1484: for (idx = 0; result && idx < items.size(); ++idx) {
1485: item = items.itemAt(idx);
1486:
1487: CVSTracer.traceIf(request.traceProcessing,
1488: "CVSResponse: item[" + idx + "] type '"
1489: + item.getType() + "'");
1490:
1491: result = this .processResponseItem(request, response, item);
1492: }
1493:
1494: if (response.getStatus() != CVSResponse.OK) {
1495: if (request.traceProcessing)
1496: CVSTracer.traceIf(true,
1497: "CVSProject.processCVSResponse: ERROR errorCode '"
1498: + response.getErrorCode()
1499: + "' errorText '"
1500: + response.getErrorText() + "'");
1501:
1502: if (response.getErrorCode().length() > 0
1503: || response.getErrorText().length() > 0) {
1504: response.appendStderr("\nError Code '"
1505: + response.getErrorCode() + "'" + " Message '"
1506: + response.getErrorText() + "'\n");
1507: }
1508: } else {
1509: CVSTracer.traceIf(request.traceProcessing,
1510: "CVSProject.processCVSResponse: OK");
1511:
1512: if (request.handleEntries) {
1513: this .writeAdminFiles();
1514: }
1515: }
1516:
1517: // REVIEW - Should error results with empty code and text
1518: // be ignored by default? This is the 'Diff' case!
1519: //
1520: // Actually, I think I want to special case 'diff'
1521: // here and use the 'ERROR' status to indicate that
1522: // there were 'no differences'. I think I can check
1523: // for an empty 'code' or 'message' to confirm that
1524: // it is 'no diffs' case and not 'some diff error'.
1525:
1526: if (request.ignoreResult) {
1527: response.setStatus(CVSResponse.OK);
1528: }
1529:
1530: return result;
1531: }
1532:
1533: private boolean processResponseItem(CVSRequest request,
1534: CVSResponse response, CVSResponseItem item) {
1535: int idx;
1536: boolean ok;
1537: CVSEntry entry = null;
1538: boolean result = true;
1539: File localFile = null;
1540:
1541: //
1542: // HACK
1543: // NOTE
1544: //
1545: // This is a special hack to accomodate the one compromise we needed
1546: // to make to get all of the path handling to work. We wrote the one
1547: // directive that
1548: //
1549: // ALL LOCAL DIRECTORY NAMES MUST BEGIN WITH "./"
1550: //
1551: // This make every case of the hideous paths returned by the server
1552: // work for us, since we are not like UNIX which works in a strickly
1553: // "relative" sense. We work from an "absolute" sense, for better or
1554: // worse...
1555: //
1556: // SPECIAL CASE
1557: //
1558: // There are times when the server will return a response item with
1559: // a repositry path ending with "./". This is usually our bad in the
1560: // protocol, but it is easy to catch and fix, so...
1561: //
1562: if (item.getPathName().endsWith("./")) {
1563: item.setPathName(item.getPathName().substring(0,
1564: item.getPathName().length() - 2));
1565:
1566: CVSTracer.traceIf(this .deepDebug,
1567: "\nPROCESSResponseItem: STRIPPED FINAL './' CASE\n"
1568: + " item.pathName = '"
1569: + item.getPathName() + "'");
1570: }
1571:
1572: if (!item.getPathName().startsWith("./")) {
1573: String itemRepos = item.getRepositoryName();
1574: int slashIdx = itemRepos.lastIndexOf("/");
1575: if (slashIdx != -1) {
1576: itemRepos = itemRepos.substring(0, slashIdx);
1577: }
1578:
1579: CVSEntry hackEntry = this .reversePathTableEntry(itemRepos);
1580:
1581: CVSTracer.traceIf(this .deepDebug,
1582: "\nPROCESSResponseItem: APPLY ITEM PATHNAME HACK\n"
1583: + " item.pathName = '"
1584: + item.getPathName()
1585: + "'\n"
1586: + " item.repos = '"
1587: + item.getRepositoryName()
1588: + "'\n"
1589: + " lookup repos = '"
1590: + itemRepos
1591: + "'\n"
1592: + " pathTable.entry:\n"
1593: + (hackEntry == null ? " NULL"
1594: : hackEntry.dumpString(" ")));
1595:
1596: if (hackEntry != null) {
1597: item.setPathName(hackEntry.getLocalDirectory());
1598: CVSTracer
1599: .traceIf(this .deepDebug,
1600: "\nPROCESSResponseItem: ITEM PATH set to '"
1601: + hackEntry.getLocalDirectory()
1602: + "'\n");
1603: } else {
1604: //
1605: // NOTE
1606: // If we did not find the repository pathname, then this item
1607: // is something we have never seen before. This should ONLY
1608: // happen during things like checkout, where the tree does not
1609: // exist yet. In these cases. prepending "./" to the local
1610: // directory appears to be the correct answer.
1611: //
1612: item.setPathName("./" + item.getPathName());
1613: CVSTracer.traceIf(this .deepDebug,
1614: "\nPROCESSResponseItem: NO PATH TABLE ENTRY, PREFIX w/ './'\n"
1615: + " ITEM PATH set to '"
1616: + item.getPathName() + "'");
1617: }
1618: }
1619:
1620: CVSTracer.traceIf(this .deepDebug, "PROCESSResponseItem:\n"
1621: + " item.getType '" + item.getType() + "'\n"
1622: + " item.getPathName '" + item.getPathName()
1623: + "'\n" + " item.repositoryName '"
1624: + item.getRepositoryName() + "'\n"
1625: + " item.getModeLine '" + item.getModeLine()
1626: + "'\n" + " item.getEntriesLine '"
1627: + item.getEntriesLine() + "'");
1628:
1629: switch (item.getType()) {
1630: case CVSResponseItem.CHECKED_IN:
1631: // Checked-in implies the file is up-to-date
1632: CVSTracer.traceIf(request.traceProcessing,
1633: "CHECKED_IN: pathName '" + item.getPathName()
1634: + "'\n repository "
1635: + item.getRepositoryName()
1636: + "'\n entryLine "
1637: + item.getEntriesLine());
1638:
1639: if (request.handleEntries) {
1640: entry = this .createItemEntry(item);
1641: if (entry != null) {
1642: CVSTracer.traceIf(request.traceProcessing,
1643: "CHECKED_IN: entry '" + entry.getFullName()
1644: + "'");
1645:
1646: localFile = this .getEntryFile(entry);
1647:
1648: entry.setTimestamp(localFile);
1649:
1650: this .updateEntriesItem(entry);
1651: }
1652: }
1653: break;
1654:
1655: case CVSResponseItem.NOTIFIED:
1656: CVSTracer.traceIf(request.traceProcessing,
1657: "NOTIFIED: pathName '" + item.getPathName()
1658: + "'\n repository '"
1659: + item.getRepositoryName() + "'");
1660:
1661: this .processNotified(item);
1662: break;
1663:
1664: case CVSResponseItem.CHECKSUM:
1665: // UNDONE
1666: break;
1667:
1668: case CVSResponseItem.COPY_FILE:
1669: CVSTracer.traceIf(request.traceProcessing,
1670: "COPY-FILE: pathName '" + item.getPathName()
1671: + "'\n newName '"
1672: + item.getNewName() + "'");
1673:
1674: //
1675: // UNDONE - it would be nice if we had a better
1676: // error report, but we do not have the
1677: // response object available deep in the
1678: // method call, and we do not throw an
1679: // exception, which may have been a better
1680: // choice than returning false.... (duh)
1681: //
1682: if (!this .performCopyFile(item)) {
1683: response.appendStderr("ERROR copying file '"
1684: + item.getPathName() + "' to '"
1685: + item.getNewName() + "'.");
1686: }
1687: break;
1688:
1689: case CVSResponseItem.CLEAR_STICKY:
1690: CVSTracer.traceIf(request.traceProcessing,
1691: "Clear-sticky: pathName '" + item.getPathName()
1692: + "'\n");
1693: this .setSticky(item, false, request.handleEntries);
1694: break;
1695:
1696: case CVSResponseItem.SET_STICKY:
1697: CVSTracer.traceIf(request.traceProcessing,
1698: "Set-sticky: pathName '" + item.getPathName()
1699: + "'\n");
1700: this .setSticky(item, true, request.handleEntries);
1701: break;
1702:
1703: case CVSResponseItem.CLEAR_STATIC_DIR:
1704: CVSTracer.traceIf(request.traceProcessing,
1705: "Clear-static-directory: pathName '"
1706: + item.getPathName() + "'\n");
1707: this .setStaticDirectory(item, false, request.handleEntries);
1708: break;
1709:
1710: case CVSResponseItem.SET_STATIC_DIR:
1711: CVSTracer.traceIf(request.traceProcessing,
1712: "Set-static-directory: pathName '"
1713: + item.getPathName() + "'\n");
1714: this .setStaticDirectory(item, true, request.handleEntries);
1715: break;
1716:
1717: case CVSResponseItem.MODULE_EXPANSION:
1718: // UNDONE
1719: break;
1720:
1721: case CVSResponseItem.NEW_ENTRY:
1722: // New-entry implies the file is still NOT up-to-date
1723: CVSTracer.traceIf(request.traceProcessing,
1724: "NEW_ENTRY: name '" + item.getPathName()
1725: + "' entryLine '" + item.getEntriesLine()
1726: + "'");
1727:
1728: if (request.handleEntries) {
1729: entry = this .createItemEntry(item);
1730: if (entry != null) {
1731: this .updateEntriesItem(entry);
1732: }
1733: }
1734: break;
1735:
1736: case CVSResponseItem.REMOVED:
1737: CVSTracer.traceIf(request.traceProcessing, "REMOVED: "
1738: + item.getPathName());
1739:
1740: if (request.handleEntries) {
1741: this .removeEntriesItem(item);
1742: }
1743: break;
1744:
1745: case CVSResponseItem.REMOVE_ENTRY:
1746: CVSTracer.traceIf(request.traceProcessing, "REMOVE_ENTRY: "
1747: + item.getPathName());
1748:
1749: if (request.handleEntries) {
1750: this .removeEntriesItem(item);
1751: }
1752: break;
1753:
1754: case CVSResponseItem.VALID_REQUESTS:
1755: // clients don't implement this.
1756: break;
1757:
1758: case CVSResponseItem.SET_CHECKIN_PROG:
1759: if (request.handleFlags) {
1760: request.setCheckInProgram(item.getPathName());
1761: }
1762: break;
1763:
1764: case CVSResponseItem.SET_UPDATE_PROG:
1765: if (request.handleFlags) {
1766: request.setUpdateProgram(item.getPathName());
1767: }
1768: break;
1769:
1770: case CVSResponseItem.PATCHED:
1771: CVSTracer.traceIf(true,
1772: "CVSProject.CVSResponseItem.PATCHED '"
1773: + item.getEntriesLine() + "' "
1774: + "PATCHED currently unimplemented.\n"
1775: + "WE SHOULD NOT BE GETTING THIS!!!");
1776:
1777: response
1778: .appendStderr("The 'Patched' response is not implemented:\n"
1779: + " '" + item.getEntriesLine() + "'");
1780: break;
1781:
1782: case CVSResponseItem.CREATED:
1783: case CVSResponseItem.MERGED:
1784: case CVSResponseItem.UPDATED:
1785: case CVSResponseItem.UPDATE_EXISTING:
1786: if (request.handleUpdated) {
1787: String cmdName = (item.getType() == CVSResponseItem.CREATED ? "Created"
1788: : (item.getType() == CVSResponseItem.MERGED ? "Merged"
1789: : (item.getType() == CVSResponseItem.UPDATED ? "Updated"
1790: : "Updated existing")));
1791:
1792: entry = this .createItemEntry(item);
1793: if (entry != null) {
1794: // We have to save this state, since the set
1795: // of the timestamp from the local file will
1796: // clear it in the entry.
1797: boolean isInConflict = entry.isInConflict();
1798:
1799: ok = this .ensureEntryHierarchy(item.getPathName(),
1800: item.getRepositoryPath());
1801:
1802: localFile = this .getEntryFile(entry);
1803:
1804: if (ok) {
1805: ok = this .ensureLocalTree(localFile,
1806: request.handleEntries);
1807: }
1808:
1809: if (localFile.exists()) {
1810: entry.setTimestamp(localFile);
1811: }
1812:
1813: if (ok) {
1814: request.getUserInterface()
1815: .uiDisplayProgressMsg(
1816: cmdName + " local file '"
1817: + localFile.getPath()
1818: + "'.");
1819:
1820: // UNDONE try/catch for better messaging!!!
1821: ok = this .updateLocalFile(item, entry,
1822: localFile);
1823: }
1824:
1825: if (ok) {
1826: if (isInConflict) {
1827: entry.setConflict(localFile);
1828: } else if (item.getType() == CVSResponseItem.MERGED) {
1829: entry.setTimestamp("Result of merge");
1830: } else {
1831: entry.setTimestamp(localFile);
1832: }
1833:
1834: if (request.handleEntries) {
1835: this .updateEntriesItem(entry);
1836: }
1837: } else {
1838: CVSLog
1839: .logMsg("CVSResponse: ERROR merging local file '"
1840: + entry.getFullName() + "'");
1841:
1842: response
1843: .appendStderr("ERROR failed updating local file '"
1844: + localFile.getPath() + "'.");
1845:
1846: result = false;
1847: }
1848: } else {
1849: CVSLog
1850: .logMsg("CVSResponse: ERROR creating item entry '"
1851: + item.toString() + "'");
1852: result = false;
1853: }
1854: }
1855: break;
1856:
1857: } // end of switch ( item type )
1858:
1859: return result;
1860: }
1861:
1862: public boolean performCopyFile(CVSResponseItem item) {
1863: boolean result = true;
1864:
1865: CVSEntry entry = this .createItemEntry(item);
1866:
1867: if (entry != null) {
1868: File fromFile = this .getEntryFile(entry);
1869:
1870: entry.setName(item.getNewName());
1871:
1872: File toFile = this .getEntryFile(entry);
1873:
1874: if (fromFile.exists()) {
1875: // REVIEW - with Jim Kingdon
1876: // wouldn't it simply be more efficient to rename?
1877: // boolean err = fromFile.renameTo( toFile );
1878: result = this .copyFileRaw(fromFile, toFile, item
1879: .isGZIPed());
1880:
1881: if (!result) {
1882: CVSLog
1883: .logMsg("CVSProject.performCopyFile: ERROR renaming '"
1884: + fromFile.getPath()
1885: + "' to '"
1886: + toFile.getPath() + "'");
1887: }
1888: } else {
1889: CVSLog.logMsg("CVSProject.performCopyFile: file '"
1890: + fromFile.getPath() + "' does not exist!");
1891: }
1892: } else {
1893: CVSTracer.traceWithStack("WHY is this entry NULL?! item '"
1894: + item.toString() + "'");
1895: }
1896:
1897: return result;
1898: }
1899:
1900: public boolean setSticky(CVSResponseItem item, boolean isSet,
1901: boolean writeFile) {
1902: boolean result;
1903:
1904: String localDir = this .normalizeLocalDirectory(item
1905: .getPathName(), item.getRepositoryPath());
1906:
1907: result = this .ensureEntryHierarchy(localDir, item
1908: .getRepositoryPath());
1909:
1910: if (result) {
1911: result = this .ensureProperWorkingDirectory(
1912: this .localRootDirectory, localDir, writeFile);
1913: }
1914:
1915: if (result && writeFile) {
1916: CVSEntry entry = this .createItemEntry(item);
1917: if (entry != null) {
1918: entry.setName("CVS/Tag");
1919: File file = this .getEntryFile(entry);
1920:
1921: if (isSet) {
1922: if (!file.exists()) {
1923: try {
1924: CVSCUtilities.writeStringFile(file, item
1925: .getTagSpec());
1926: } catch (IOException ex) {
1927: CVSTracer
1928: .traceWithStack("ERROR writing sticky tag file '"
1929: + file.getPath()
1930: + "', "
1931: + ex.getMessage());
1932: }
1933: }
1934: } else {
1935: if (file.exists()) {
1936: file.delete();
1937: }
1938: }
1939: }
1940: } else if (!result) {
1941: CVSTracer.traceWithStack("ensureEntryHierarchy( '"
1942: + item.getPathName() + "', '"
1943: + item.getRepositoryPath() + "' ) FAILED");
1944: }
1945:
1946: return result;
1947: }
1948:
1949: public boolean setStaticDirectory(CVSResponseItem item,
1950: boolean isSet, boolean writeFile) {
1951: boolean result;
1952:
1953: result = this .ensureEntryHierarchy(item.getPathName(), item
1954: .getRepositoryPath());
1955:
1956: if (result) {
1957: result = this .ensureProperWorkingDirectory(
1958: this .localRootDirectory, this
1959: .normalizeLocalDirectory(
1960: item.getPathName(), item
1961: .getRepositoryPath()),
1962: writeFile);
1963: }
1964:
1965: if (result && writeFile) {
1966: CVSEntry entry = this .createItemEntry(item);
1967: if (entry != null) {
1968: entry.setName("CVS/Entries.static");
1969: File file = this .getEntryFile(entry);
1970:
1971: if (isSet) {
1972: if (!file.exists()) {
1973: CVSCUtilities.createEmptyFile(file);
1974: }
1975: } else {
1976: if (file.exists()) {
1977: file.delete();
1978: }
1979: }
1980: }
1981: } else if (!result) {
1982: CVSTracer.traceWithStack("ensureEntryHierarchy( '"
1983: + item.getPathName() + "', '"
1984: + item.getRepositoryPath() + "' ) FAILED");
1985: }
1986:
1987: return result;
1988: }
1989:
1990: public CVSNotifyItem parseNotifyLine(String notifyLine) {
1991: CVSNotifyItem result = null;
1992:
1993: String notType = notifyLine.substring(0, 1);
1994: notifyLine = notifyLine.substring(1);
1995:
1996: StringTokenizer toker = new StringTokenizer(notifyLine, "\t");
1997:
1998: int count = toker.countTokens();
1999:
2000: if (count > 3) {
2001: String name = null;
2002: String time = null;
2003: String host = null;
2004: String wdir = null;
2005: String watches = null;
2006:
2007: try {
2008: name = toker.nextToken();
2009: time = toker.nextToken();
2010: host = toker.nextToken();
2011: wdir = toker.nextToken();
2012: } catch (NoSuchElementException ex) {
2013: name = null;
2014: }
2015:
2016: try {
2017: watches = toker.nextToken();
2018: } catch (NoSuchElementException ex) {
2019: watches = null;
2020: }
2021:
2022: if (name != null && time != null && host != null
2023: && wdir != null) {
2024: CVSEntry entry = (CVSEntry) this .pathTable.get(wdir);
2025:
2026: if (entry != null) {
2027: result = new CVSNotifyItem(notType, name, time,
2028: host, wdir,
2029: (watches == null ? "" : watches), entry
2030: .getRepository());
2031: }
2032: }
2033: }
2034:
2035: return result;
2036: }
2037:
2038: protected boolean processNotified(CVSResponseItem item) {
2039: boolean result = true;
2040: BufferedReader read;
2041: PrintWriter write;
2042: String inline;
2043:
2044: CVSEntry entry = this .createItemEntry(item);
2045:
2046: String itemPath = entry.getFullName();
2047:
2048: String fileName = CVSProject.getAdminNotifyPath(CVSProject
2049: .rootPathToAdminPath(this .getLocalRootPath()));
2050:
2051: File notFile = new File(fileName);
2052: File tmpFile = new File(fileName + ".tmp");
2053:
2054: try {
2055: read = new BufferedReader(new FileReader(notFile));
2056: write = new PrintWriter(new FileWriter(tmpFile));
2057: } catch (IOException ex) {
2058: String msg = "ERROR opening Notification file '" + fileName
2059: + "' for Notified response.";
2060: CVSLog.logMsg(msg);
2061: CVSTracer.traceWithStack(msg);
2062: return false;
2063: }
2064:
2065: int count = 0;
2066: for (boolean chk = true;;) {
2067: try {
2068: inline = read.readLine();
2069: } catch (IOException ex) {
2070: String msg = "ERROR reading Notification file "
2071: + "during Notified response.";
2072: CVSLog.logMsg(msg);
2073: CVSTracer.traceWithStack(msg);
2074: inline = null;
2075: }
2076:
2077: if (inline == null)
2078: break;
2079:
2080: if (!chk) {
2081: write.println(inline);
2082: count++;
2083: } else {
2084: CVSNotifyItem notifyItem = this .parseNotifyLine(inline);
2085:
2086: if (notifyItem != null) {
2087: String fullName = notifyItem.getWorkingDirectory()
2088: + notifyItem.getName();
2089:
2090: if (!itemPath.equals(fullName)) {
2091: write.println(inline);
2092: chk = false;
2093: count++;
2094: }
2095: } else {
2096: CVSLog.logMsg("ERROR, bad line in 'CVS/Notify':\n"
2097: + " File: '" + fileName + "'\n"
2098: + " Line: " + inline);
2099: }
2100: }
2101: }
2102:
2103: try {
2104: read.close();
2105: } catch (IOException ex) {
2106: }
2107:
2108: write.flush();
2109: write.close();
2110:
2111: if (result) {
2112: result = notFile.delete();
2113: if (result) {
2114: if (count > 0)
2115: result = tmpFile.renameTo(notFile);
2116: else
2117: tmpFile.delete();
2118: }
2119: }
2120:
2121: return result;
2122: }
2123:
2124: public String readRootDirectory(File rootFile) {
2125: String result = null;
2126: try {
2127: result = CVSCUtilities.readStringFile(rootFile);
2128: } catch (IOException ex) {
2129: result = null;
2130: }
2131:
2132: return result;
2133: }
2134:
2135: public String readRepository(File reposFile) {
2136: String result = null;
2137: try {
2138: result = CVSCUtilities.readStringFile(reposFile);
2139: } catch (IOException ex) {
2140: result = null;
2141: }
2142:
2143: return result;
2144: }
2145:
2146: /**
2147: *
2148: * @param repository The server's repository pathname for the root.
2149: */
2150: public void establishRootEntry(String repository) {
2151: if (CVSProject.deepDebug)
2152: CVSTracer.traceIf(true, "CVSProject.establishRootEntry: "
2153: + "repository '" + repository + "'");
2154:
2155: CVSEntry rootEntry = new CVSEntry();
2156:
2157: rootEntry.setDirty(true);
2158: rootEntry.setName(".");
2159: rootEntry.setRepository(repository);
2160: rootEntry.setLocalDirectory("./");
2161:
2162: // We need to set the Entry List to mark this as a directory.
2163: rootEntry.setDirectoryEntryList(new CVSEntryVector());
2164:
2165: this .pathTable.put(rootEntry.getLocalDirectory(), rootEntry);
2166:
2167: if (CVSProject.deepDebug)
2168: CVSTracer.traceIf(true,
2169: "CVSProject.establishRootEntry: ROOT ESTABLISHED:\n"
2170: + rootEntry.dumpString(" "));
2171:
2172: this .rootEntry = rootEntry;
2173: }
2174:
2175: public void openProject(File localRootFile) throws IOException {
2176: String repositoryStr;
2177: String rootDirectoryStr;
2178:
2179: if (this .deepDebug)
2180: CVSTracer.traceIf(true,
2181: "CVSProject.openProject: OPEN PROJECT '"
2182: + localRootFile.getPath() + "'");
2183:
2184: File adminDirFile = new File(localRootFile.getPath(), "CVS");
2185:
2186: if (this .deepDebug)
2187: CVSTracer.traceIf(true,
2188: "CVSProject.openProject: adminDirFile '"
2189: + adminDirFile.getPath() + "'");
2190:
2191: if (!adminDirFile.exists())
2192: throw new IOException("admin directory '"
2193: + adminDirFile.getPath() + "' does not exist");
2194:
2195: String rootPath = CVSProject.getAdminRootPath(CVSCUtilities
2196: .importPath(adminDirFile.getPath()));
2197:
2198: File adminRootFile = new File(CVSCUtilities
2199: .exportPath(rootPath));
2200:
2201: if (this .deepDebug)
2202: CVSTracer.traceIf(true,
2203: "CVSProject.openProject: adminRootFile '"
2204: + adminRootFile.getPath() + "'");
2205:
2206: if (!adminRootFile.exists())
2207: throw new IOException("admin Root file '"
2208: + adminRootFile.getPath() + "' does not exist");
2209:
2210: String reposPath = CVSProject
2211: .getAdminRepositoryPath(adminDirFile.getPath());
2212:
2213: File adminRepositoryFile = new File(reposPath);
2214:
2215: if (this .deepDebug)
2216: CVSTracer.traceIf(true,
2217: "CVSProject.openProject: adminRepositoryFile '"
2218: + adminRepositoryFile.getPath() + "'");
2219:
2220: if (!adminRepositoryFile.exists())
2221: throw new IOException("admin Repository file '"
2222: + adminRepositoryFile.getPath()
2223: + "' does not exist");
2224:
2225: rootDirectoryStr = this .readRootDirectory(adminRootFile);
2226: if (rootDirectoryStr == null)
2227: throw new IOException("could not read admin Root file '"
2228: + adminRootFile.getPath() + "'");
2229:
2230: repositoryStr = this .readRepository(adminRepositoryFile);
2231: if (repositoryStr == null)
2232: throw new IOException(
2233: "could not read admin Repository file '"
2234: + adminRepositoryFile.getPath() + "'");
2235:
2236: if (this .deepDebug)
2237: CVSTracer.traceIf(true,
2238: "CVSProject.openProject: Read Admin directory\n"
2239: + " rootPath '" + rootPath + "'\n"
2240: + " reposPath '" + reposPath + "'\n"
2241: + " rootDirStr '" + rootDirectoryStr
2242: + "'\n" + " reposStr '" + repositoryStr
2243: + "'");
2244:
2245: this .projectDef = new CVSProjectDef(rootDirectoryStr,
2246: repositoryStr);
2247:
2248: if (!this .projectDef.isValid())
2249: throw new IOException(
2250: "could not parse project specification, "
2251: + this .projectDef.getReason());
2252:
2253: this .isPServer = this .projectDef.isPServer();
2254: this .connMethod = this .projectDef.getConnectMethod();
2255: this .userName = this .projectDef.getUserName();
2256: this .getClient().setHostName(this .projectDef.getHostName());
2257:
2258: rootDirectoryStr = this .projectDef.getRootDirectory();
2259:
2260: //
2261: // REVIEW
2262: // Should I not remove the check for the '/' or 'C:/'
2263: // at the beginning and just make the "starts with root
2264: // directory" check in stead, and then if that does not
2265: // match, see if the string starts with 'C:/' and if not
2266: // assume relative at that point? Would that not make
2267: // the code less dependent on what are valid "starts"?
2268: //
2269: // The previous check, that is started with '/', does
2270: // not work for the case of a Win32 server, since it
2271: // will have a root such as 'C:/cvs'. Thus, we now
2272: // check for "starts with /", as well as "starts with
2273: // drive letter colon slash".
2274: //
2275: // Thanks to Manfred Usselmann <Usselmann.M@icg-online.de>
2276: // for these patches.
2277: //
2278: // OLD CODE:
2279: // if ( repositoryStr.startsWith( "/" ) )
2280: //
2281: // Contributed CODE:
2282: // char ch0 = repositoryStr.charAt(0);
2283: // char ch1 = repositoryStr.charAt(1);
2284: // char ch2 = repositoryStr.charAt(2);
2285: // if ( ch0 == '/'
2286: // // IF there a colon for the drive letter
2287: // || ( ch1 == ':'
2288: // // AND there is a valid drive letter
2289: // && ( ( ch0 >= 'a' && ch0 <= 'z' )
2290: // || ( ch0 >= 'A' && ch0 <= 'Z' ) )
2291: // // AND there is a slash or backslash
2292: // && ( ch2 == '/' || ch2 == '\\' ) ) )
2293: // {
2294: // if ( ! repositoryStr.startsWith( rootDirectoryStr ) )
2295: // {
2296: // throw new IOException
2297: // ( "full repository path '" + repositoryStr
2298: // + "' does not start with Root path '"
2299: // + rootDirectoryStr + "'" );
2300: // }
2301: // }
2302: // else
2303: // {
2304: // // The relative pathname case, prepend with the root.
2305: // repositoryStr = rootDirectoryStr + "/" + repositoryStr;
2306: // }
2307: //
2308:
2309: if (repositoryStr.startsWith(rootDirectoryStr)) {
2310: // The full pathname case. No adjustment needed.
2311: } else {
2312: // The relative pathname case, prepend with the root.
2313: repositoryStr = rootDirectoryStr + "/" + repositoryStr;
2314: }
2315:
2316: //
2317: // REVIEW
2318: // UNDONE - need 'computeParentDirectory()' here.
2319: // File.getParent() does not seem to work.
2320: // I suspect because it uses the local separator?
2321: //
2322: // Should really just get the module name from the local dir name.?
2323: //
2324: String localRootStr = CVSCUtilities.importPath(localRootFile
2325: .getPath());
2326:
2327: int index = localRootStr.lastIndexOf('/');
2328:
2329: // This will include the beginning slash is there is any string at all...
2330: String repos = repositoryStr.substring(rootDirectoryStr
2331: .length());
2332:
2333: if (this .deepDebug)
2334: CVSTracer.traceIf(true,
2335: "CVSProject.openProject: LOCAL ROOT CHECK\n"
2336: + " localRootStr '" + localRootStr
2337: + "'\n" + " repos '" + repos
2338: + "'");
2339: /*
2340: ** REL 5.0.7
2341: **
2342: if ( repos.length() > 0 && localRootStr.endsWith( repos ) )
2343: {
2344: localRootStr =
2345: localRootStr.substring
2346: ( 0, ( localRootStr.length() - repos.length() ) );
2347: }
2348: **
2349: */
2350: if (this .deepDebug)
2351: CVSTracer.traceIf(true,
2352: "CVSProject.openProject: Establish ROOT\n"
2353: + " localRootStr '" + localRootStr
2354: + "'\n" + " repositoryStr '"
2355: + repositoryStr + "'\n"
2356: + " rootDirStr '" + rootDirectoryStr
2357: + "'\n" + " repos '" + repos
2358: + "'");
2359:
2360: if (repos.startsWith("/"))
2361: repos = repos.substring(1);
2362: if (repos.length() < 1)
2363: repos = ".";
2364:
2365: this .setRepository(repos); // This is just a "name" now...
2366:
2367: this .setLocalRootDirectory(localRootStr);
2368:
2369: this .setRootDirectory(rootDirectoryStr);
2370:
2371: this .establishRootEntry(rootDirectoryStr);
2372: /*
2373: ** REL5.0.6
2374: **
2375: if ( this.deepDebug )
2376: CVSTracer.traceIf( true,
2377: "CVSProject.openProject: Establish Child?\n"
2378: + " repos '" + repos + "'\n"
2379: + " repositoryStr '" + repositoryStr + "'" );
2380:
2381: if ( repos.length() > 0 )
2382: {
2383: CVSEntry childEntry = new CVSEntry();
2384:
2385: int slashIdx = repos.lastIndexOf( "/" );
2386:
2387: if ( slashIdx == -1 )
2388: childEntry.setName( repos );
2389: else
2390: childEntry.setName( repos.substring( slashIdx + 1 ) );
2391:
2392: childEntry.setRepository( repositoryStr );
2393: childEntry.setLocalDirectory( "." + repos + "/" );
2394: childEntry.setDirectoryEntryList( new CVSEntryVector() );
2395:
2396: CVSEntryVector eV = new CVSEntryVector();
2397: eV.appendEntry( childEntry );
2398:
2399: if ( this.deepDebug )
2400: CVSTracer.traceIf( true,
2401: "CVSProject.openProject: ROOT CHILD: \n"
2402: + childEntry.dumpString() );
2403:
2404: this.rootEntry.setDirectoryEntryList( eV );
2405: }
2406: **
2407: */
2408: if (CVSProject.deepDebug)
2409: CVSTracer.traceIf(true, "CVSProject.openProject:\n"
2410: + " Root Directory: " + this .rootDirectory
2411: + "\n" + " Repository: " + this .repository
2412: + "\n" + " rootRepos: " + repositoryStr
2413: + "\n" + " Local Root: "
2414: + this .localRootDirectory + "\n");
2415:
2416: if (CVSProject.deepDebug)
2417: CVSTracer.traceIf(true,
2418: "CVSProject.openProject: ROOT ENTRY\n"
2419: + this .rootEntry.dumpString());
2420:
2421: if (!readEntries()) {
2422: throw new IOException("ERROR reading 'Entries' file ");
2423: }
2424:
2425: if (CVSProject.deepDebug) {
2426: StringBuffer buf = new StringBuffer();
2427:
2428: this .dumpCVSProject(buf, "Project Open");
2429:
2430: CVSLog.logMsg(buf.toString());
2431: }
2432: }
2433:
2434: public void removeAllEntries() {
2435: this .rootEntry.removeAllEntries();
2436: }
2437:
2438: public void addNewEntry(CVSEntry entry) {
2439: if (this .rootEntry == null) {
2440: CVSTracer
2441: .traceWithStack("CVSProject.addNewEntry: NULL ROOT ENTRY!!!!");
2442: }
2443:
2444: String name = entry.getName();
2445: String localDirectory = entry.getLocalDirectory();
2446: String repository = entry.getRepository();
2447:
2448: this .ensureEntryHierarchy(localDirectory, repository);
2449:
2450: CVSEntry parentEntry = (CVSEntry) this
2451: .getPathTableEntry(localDirectory);
2452:
2453: if (parentEntry == null) {
2454: CVSTracer.traceWithStack("ENTRY '" + entry.getFullName()
2455: + "' NO PARENT!");
2456: return;
2457: }
2458:
2459: parentEntry.appendEntry(entry);
2460: }
2461:
2462: public String reposNameToRepository(String fullRepos) {
2463: int index = fullRepos.lastIndexOf('/');
2464:
2465: if (index < 0) {
2466: CVSTracer
2467: .traceWithStack("CVSProject.reposNameToRepository: ERROR "
2468: + "repository '"
2469: + fullRepos
2470: + "' has no slash!");
2471: return fullRepos;
2472: } else {
2473: return fullRepos.substring(0, index);
2474: }
2475: }
2476:
2477: public String reposNameToFileName(String fullRepos) {
2478: int index = fullRepos.lastIndexOf('/');
2479:
2480: if (index < 0) {
2481: CVSTracer
2482: .traceWithStack("CVSProject.reposNameToFileName: ERROR "
2483: + "repository '"
2484: + fullRepos
2485: + "' has no slash!");
2486: return fullRepos;
2487: } else {
2488: return fullRepos.substring(index + 1);
2489: }
2490: }
2491:
2492: public boolean removeEntriesItem(CVSResponseItem item) {
2493: CVSEntryVector entries;
2494: boolean result = true;
2495:
2496: if (CVSProject.deepDebug)
2497: CVSTracer.traceIf(true,
2498: "CVSProject.removeEntriesItem: pathName '"
2499: + item.getPathName() + "'");
2500:
2501: String localDirectory = this .normalizeLocalDirectory(item
2502: .getPathName(), this .reposNameToRepository(item
2503: .getRepositoryName()));
2504:
2505: CVSEntry parentEntry = (CVSEntry) this
2506: .getPathTableEntry(localDirectory);
2507:
2508: if (parentEntry == null) {
2509: result = false;
2510: CVSTracer
2511: .traceWithStack("CVSProject.removeEntriesItem: NO PARENT! pathName '"
2512: + item.getPathName()
2513: + "' (localDir '"
2514: + localDirectory + "').");
2515: } else {
2516: String entryName = this .reposNameToFileName(item
2517: .getRepositoryName());
2518:
2519: result = parentEntry.removeEntry(entryName);
2520:
2521: //
2522: // MTT-delete
2523: // Fixes the problem in which the file is left undeleted.
2524: //
2525: if (item.getType() == CVSResponseItem.REMOVED) {
2526: String pathName = item.getPathName();
2527: if (pathName.startsWith("./"))
2528: pathName = pathName.substring(2);
2529:
2530: String fileName = this .localRootDirectory
2531: + File.separator + pathName + entryName;
2532:
2533: File file = new File(fileName.replace('/',
2534: File.separatorChar));
2535: if (!file.delete()) {
2536: CVSTracer
2537: .traceWithStack("CVSProject.removeEntriesItem: Could not delete file "
2538: + file.getAbsolutePath());
2539: }
2540: }
2541: }
2542:
2543: return result;
2544: }
2545:
2546: /**
2547: * Given an entry, update the entry in our project. If the
2548: * entry is new (not found), then add it to our project.
2549: *
2550: * @param newEntry The entry to update.
2551: */
2552:
2553: public void updateEntriesItem(CVSEntry newEntry) {
2554: CVSEntry entry;
2555: boolean result;
2556:
2557: if (CVSProject.deepDebug)
2558: CVSTracer.traceIf(true,
2559: "CVSProject.UPDATEEntriesItem: newEntry\n"
2560: + " getFullName '"
2561: + newEntry.getFullName() + "'\n"
2562: + " getLocalDirectory '"
2563: + newEntry.getLocalDirectory() + "'\n"
2564: + " getAdminEntryLine '"
2565: + newEntry.getAdminEntryLine() + "'");
2566:
2567: // REVIEW
2568: // UNDONE
2569: //
2570: // When we get these from the server, typically they
2571: // have only the first two fields filled in.
2572: //
2573: // Therefore, I think it is correct to simply bother
2574: // with the possible effects that these two first
2575: // fields can have on our local Entries.
2576: //
2577: // Note: When the add command is used, it sends back
2578: // a 'Checked-in' with an Entry '/name/0///'.
2579: // CVSEntries are smart enough to recognize
2580: // new user files and set conflict to 'Initial...'
2581: // when needed.
2582: //
2583: // Also, when the 'remove' command is performed,
2584: // it sends back a 'Checked-in' with an Entry
2585: // '/name/-version///'. We have to be smart enough
2586: // here to pickup the "marked for removal" aspect
2587: // only if our version match. God only knows why
2588: // we would ever get one that did not match our Entry...
2589:
2590: String name = newEntry.getName();
2591: String localDirectory = newEntry.getLocalDirectory();
2592:
2593: if (CVSProject.deepDebug)
2594: CVSTracer.traceIf(true,
2595: "CVSProject.updateEntriesItem: localDirectory '"
2596: + localDirectory + "' name '" + name
2597: + "' ENTRY '" + newEntry + "'");
2598:
2599: CVSEntry parentEntry = (CVSEntry) this
2600: .getPathTableEntry(localDirectory);
2601:
2602: entry = null;
2603: if (parentEntry != null) {
2604: entry = parentEntry.locateEntry(name);
2605: }
2606:
2607: if (CVSProject.deepDebug)
2608: CVSTracer.traceIf(true,
2609: "CVSProject.updateEntriesItem: Parent '"
2610: + (parentEntry == null ? "(null)"
2611: : parentEntry.getFullName()) + "'");
2612:
2613: if (entry != null) {
2614: // New user files are special here, since typically
2615: // their version string is empty (null). They can not
2616: // really have a conflict
2617:
2618: if (CVSProject.deepDebug)
2619: CVSTracer.traceIf(true,
2620: "CVSProject.updateEntriesItem: newUserfile? '"
2621: + (newEntry.isNewUserFile() ? "yes"
2622: : "no") + "'");
2623:
2624: if (!newEntry.isNewUserFile()
2625: && !entry.getVersion()
2626: .equals(newEntry.getVersion())) {
2627: if (CVSProject.deepDebug)
2628: CVSTracer
2629: .traceIf(
2630: true,
2631: "CVSProject.updateEntriesItem: "
2632: + "WARNING: version mismatch: Entry '"
2633: + newEntry.getFullName()
2634: + "' New '"
2635: + newEntry.getVersion()
2636: + "' Existing: '"
2637: + entry.getVersion() + "'");
2638: }
2639:
2640: // If the new entry's conflict field is not null,
2641: // then set the existing entry's field to the same
2642: // value. Since we are checking the inConflict flag
2643: // below, we can just deal with the string part of
2644: // the conflict field. The same goes for the version.
2645:
2646: if (newEntry.getVersion() != null)
2647: entry.setVersion(newEntry.getVersion());
2648:
2649: if (newEntry.isNewUserFile())
2650: entry.setNewUserFile(true);
2651:
2652: if (newEntry.isToBeRemoved())
2653: entry.setToBeRemoved(true);
2654:
2655: // completeTimestamp() returns the 'timestamp+conflict' format,
2656: // which setTimestamp() will properly parse for conflict info.
2657: if (newEntry.completeTimestamp() != null)
2658: entry.setTimestamp(newEntry.completeTimestamp());
2659:
2660: if (newEntry.getOptions() != null
2661: && newEntry.getOptions().length() > 0)
2662: entry.setOptions(newEntry.getOptions());
2663:
2664: if (newEntry.getTag() != null) {
2665: entry.setTag(newEntry.getTag());
2666: } else if (newEntry.getDate() != null) {
2667: entry.setDate(newEntry.getDate());
2668: } else {
2669: // This one call nulls both tag and date.
2670: entry.setTag(null);
2671: }
2672:
2673: entry.setDirty(true);
2674:
2675: if (CVSProject.deepDebug)
2676: CVSTracer.traceIf(true,
2677: "CVSProject.updateEntriesItem: FINAL:\n"
2678: + " getFullName '"
2679: + entry.getFullName() + "'\n"
2680: + " getAdminEntryLine '"
2681: + entry.getAdminEntryLine() + "'\n"
2682: + " getLocalDirectory '"
2683: + entry.getLocalDirectory() + "'");
2684: } else {
2685: if (parentEntry == null) {
2686: CVSTracer
2687: .traceIf(true,
2688: "CVSProject.updateEntriesItem: PARENT IS NULL!!!");
2689: CVSTracer.traceWithStack("NULL PARENT!");
2690: }
2691: this .addNewEntry(newEntry);
2692: newEntry.setDirty(true);
2693: }
2694: }
2695:
2696: // REVIEW
2697: // This method should be throwing exceptions! not returning false.
2698:
2699: public boolean readEntries() {
2700: boolean ok = true;
2701:
2702: if (CVSProject.debugEntryIO)
2703: CVSTracer.traceIf(true, "CVSProject.readEntries:\n"
2704: + " locaRootPath '" + this .getLocalRootPath()
2705: + "'\n" + " ROOT ENTRY '"
2706: + this .rootEntry.dumpString() + "'");
2707:
2708: String rootStr = CVSCUtilities.exportPath(this
2709: .getLocalRootPath());
2710:
2711: File workingDirectory = new File(rootStr);
2712:
2713: if (CVSProject.debugEntryIO)
2714: CVSTracer.traceIf(true, "CVSProject.readEntries:\n"
2715: + " WkgDirPath '" + workingDirectory.getPath()
2716: + "'");
2717:
2718: CVSEntryVector entries = this .readEntriesFile(this .rootEntry,
2719: workingDirectory);
2720:
2721: if (entries != null)
2722: this .rootEntry.setDirectoryEntryList(entries);
2723: else
2724: return false;
2725:
2726: return true;
2727: }
2728:
2729: /**
2730: * @param dirEntry The entry of the directory being loaded.
2731: * @param workingDirectory The local file system directory of dirEntry.
2732: */
2733:
2734: public CVSEntryVector readEntriesFile(CVSEntry dirEntry,
2735: File workingDirectory) {
2736: int linenum = 0;
2737: String line = null;
2738: boolean ok = true;
2739: boolean isDir = false;
2740: BufferedReader in = null;
2741:
2742: CVSEntryVector entries = new CVSEntryVector();
2743:
2744: // Compute the 'local directory' that this Entry will exchange
2745: // with the server during the protocol...
2746:
2747: String localDirectory = CVSCUtilities
2748: .importPath(workingDirectory.getPath().substring(
2749: this .localRootDirectory.length()));
2750:
2751: if (localDirectory.startsWith("/"))
2752: localDirectory = localDirectory.substring(1);
2753:
2754: localDirectory = CVSCUtilities.ensureFinalSlash("./"
2755: + localDirectory);
2756:
2757: if (CVSProject.debugEntryIO)
2758: CVSTracer.traceIf(true,
2759: "CVSProject.readEntriesFile: ENTER\n"
2760: + " wkgDir '"
2761: + workingDirectory.getPath() + "'\n"
2762: + " localDir '" + localDirectory + "'\n"
2763: + " dirEntry\n"
2764: + dirEntry.dumpString(" "));
2765:
2766: // =============== ROOT ======================
2767: String adminRootPath = CVSProject
2768: .rootPathToAdminPath(CVSCUtilities
2769: .importPath(workingDirectory.getPath()));
2770:
2771: File adminRootFile = new File(CVSCUtilities
2772: .exportPath(CVSProject.getAdminRootPath(adminRootPath)));
2773:
2774: if (CVSProject.debugEntryIO)
2775: CVSTracer.traceIf(true,
2776: "CVSProject.readEntriesFile: adminRootFile '"
2777: + adminRootFile.getPath() + "'\n");
2778:
2779: String rootDirectoryStr = this .readRootDirectory(adminRootFile);
2780:
2781: if (rootDirectoryStr == null) {
2782: CVSLog.logMsg("ERROR admin 'Root' file '"
2783: + adminRootFile.getPath() + "' is empty!");
2784: return null;
2785: }
2786:
2787: //
2788: // We had to modify this code to accomodate Win32 servers.
2789: // They have root directories such as 'C:\projects\cvs'.
2790: // Thus, you end up with cvs specifications such as this:
2791: // :pserver:user@host.domain:C:/src/cvs
2792: //
2793: // In that case, the following commented line of code would
2794: // incorrectly use the path *after* the drive letter + colon,
2795: // which of course if not correct and hoses the server and
2796: // all of our root prefix checks!
2797: //
2798: // To fix the code, we change the code to count three colons
2799: // to the right from the left, as opposed to the old code
2800: // which counted one left from the right.
2801: //
2802: // Thanks to Manfred Usselmann <Usselmann.M@icg-online.de>
2803: // for solving this and providing the patch.
2804: //
2805: // int index = rootDirectoryStr.lastIndexOf( ':' );
2806: //
2807: // REVIEW This patch was submitted, but I do not understand
2808: // in what context it ever occurs to be fixed. TGE
2809: // GG-NT-no-server Gérard COLLIN <gcollin@netonomy.com>
2810: // Changed to searching first : after @ because of
2811: // "user@host.domain:C:/src/cvs" specifications
2812: //
2813: // int index = -1;
2814: // index = rootDirectoryStr.indexOf( '@');
2815: // if ( index >= 0 )
2816: // {
2817: // index = rootDirectoryStr.indexOf( ':', index+1 );
2818: // if ( index >= 0 )
2819: // {
2820: // rootDirectoryStr =
2821: // rootDirectoryStr.substring( index + 1 );
2822: // }
2823: // }
2824: //
2825:
2826: int index = -1;
2827:
2828: if (!rootDirectoryStr.startsWith(":")) {
2829: // "user@host.domain:C:/src/cvs"
2830: index = rootDirectoryStr.indexOf(':', 0);
2831: } else {
2832: for (int i = 0; i < 3; ++i) {
2833: index = rootDirectoryStr.indexOf(':', index + 1);
2834: if (index == -1)
2835: break;
2836: }
2837: }
2838:
2839: if (index >= 0) {
2840: rootDirectoryStr = rootDirectoryStr.substring(index + 1);
2841: } else {
2842: CVSLog.logMsg("ERROR admin 'Root' file is MISSING COLONS!");
2843: }
2844:
2845: // ============ REPOSITORY ===================
2846: File adminRepositoryFile = new File(CVSCUtilities
2847: .exportPath(CVSProject
2848: .getAdminRepositoryPath(adminRootPath)));
2849:
2850: if (CVSProject.debugEntryIO)
2851: CVSTracer.traceIf(true,
2852: "CVSProject.readEntriesFile: adminRepositoryFile '"
2853: + adminRepositoryFile.getPath() + "'\n");
2854:
2855: String repositoryStr = this .readRepository(adminRepositoryFile);
2856: if (repositoryStr == null) {
2857: CVSLog.logMsg("ERROR admin 'Repository' file '"
2858: + adminRepositoryFile.getPath() + "' is empty!");
2859: return null;
2860: }
2861:
2862: // NOTE
2863: // It appears that when the CVS command line checks out a
2864: // working directory, it sometimes (some versions?) sets the
2865: // repository to a path *relative* to the root directory.
2866: // Since jCVS expects a full pathname, we normalize here.
2867: //
2868: // Thanks to Thorsten Ludewig <Thorsten.Ludewig@FH-Wolfenbuettel.DE>
2869: // for sending the patch for this.
2870: //
2871: if (!repositoryStr.startsWith(rootDirectoryStr)) {
2872: // GG-dot-rep . repositoryStr should be ignored.
2873: if (repositoryStr.equals("."))
2874: repositoryStr = rootDirectoryStr;
2875: else
2876: repositoryStr = rootDirectoryStr + "/" + repositoryStr;
2877: }
2878:
2879: // ============ TABLE ENTRY ===================
2880: dirEntry.setRepository(repositoryStr);
2881: this .pathTable.put(localDirectory, dirEntry);
2882:
2883: if (CVSProject.debugEntryIO)
2884: CVSTracer.traceIf(true,
2885: "READENTRIES: ADDED PATH TABLE ENTRY\n"
2886: + " dirEntry: "
2887: + dirEntry.getFullName() + "\n"
2888: + " localDirectory: " + localDirectory
2889: + "\n" + " repository: "
2890: + repositoryStr);
2891:
2892: // ============== ENTRIES ===================
2893:
2894: // First, make sure we pick up and process Entries.Log
2895: //
2896: try {
2897: CVSCUtilities.integrateEntriesLog(new File(CVSCUtilities
2898: .exportPath(adminRootPath)));
2899: } catch (IOException ex) {
2900: CVSLog
2901: .logMsg("ERROR integrating 'Entries.Log' file in Admin Path '"
2902: + adminRootPath + "', " + ex.getMessage());
2903: ex.printStackTrace();
2904: }
2905:
2906: File entriesFile = new File(CVSCUtilities.exportPath(CVSProject
2907: .getAdminEntriesPath(adminRootPath)));
2908:
2909: if (CVSProject.debugEntryIO)
2910: CVSTracer.traceIf(true,
2911: "CVSProject.readEntriesFile: entriesFile '"
2912: + entriesFile.getPath() + "'\n");
2913:
2914: try {
2915: in = new BufferedReader(new FileReader(entriesFile));
2916: } catch (IOException ex) {
2917: in = null;
2918: ok = false;
2919: }
2920:
2921: for (linenum = 1; ok; ++linenum) {
2922: try {
2923: line = in.readLine();
2924: } catch (IOException ex) {
2925: line = null;
2926: break;
2927: }
2928:
2929: if (line == null)
2930: break;
2931:
2932: // UNDONE
2933: // We need to properly handle "D/" entries (i.e. look up subfolders
2934: // that contain CVS admin directories).
2935: //
2936: // Lines that starts with "D" are directories!
2937: if (line.startsWith("D")) {
2938: isDir = true;
2939: line = line.substring(1);
2940: }
2941:
2942: if (line.startsWith("/")) {
2943: CVSEntry entry = new CVSEntry();
2944:
2945: try {
2946: entry.parseEntryLine(line, false);
2947: } catch (ParseException ex) {
2948: // UNDONE
2949: CVSLog.logMsg("Bad admin 'Entries' line " + linenum
2950: + ", '" + line + "' isDir '" + isDir
2951: + "' - " + ex.getMessage());
2952: ok = false;
2953: }
2954:
2955: if (ok) {
2956: if (CVSProject.debugEntryIO)
2957: CVSTracer.traceIf(true,
2958: "CVSProject.readEntriesFile: PARSED ENTRY\n"
2959: + " entry: "
2960: + entry.getName() + "\n"
2961: + " repository: "
2962: + repositoryStr + "\n"
2963: + " localDirectory: "
2964: + localDirectory);
2965:
2966: entry.setRepository(repositoryStr);
2967: entry.setLocalDirectory(localDirectory);
2968:
2969: entries.appendEntry(entry);
2970:
2971: if (isDir) {
2972: String newLocal = localDirectory
2973: + entry.getName() + "/";
2974:
2975: String newRepos = repositoryStr + "/"
2976: + entry.getName();
2977:
2978: entry.setRepository(newRepos);
2979: entry.setLocalDirectory(newLocal);
2980:
2981: String newWkgPath = workingDirectory.getPath()
2982: + File.separator + entry.getName()
2983: + File.separator;
2984:
2985: String adminPath = newLocal + "CVS";
2986:
2987: File newWorking = new File(workingDirectory,
2988: entry.getName());
2989: // this.localRootDirectory + "/"
2990: // + localDirectory + entry.getName()
2991: // );
2992:
2993: if (CVSProject.debugEntryIO)
2994: CVSTracer.traceIf(true,
2995: "readEntriesFile: IS DIRECTORY:\n"
2996: + " entriesFile '"
2997: + entriesFile.getPath()
2998: + "'\n"
2999: + " NewWorkingDir '"
3000: + newWorking.getPath()
3001: + "'\n"
3002: + " newRepos '"
3003: + newRepos + "'\n"
3004: + " newLocal '"
3005: + newLocal + "'");
3006:
3007: CVSEntryVector newEntries = readEntriesFile(
3008: entry, newWorking);
3009:
3010: if (newEntries == null) {
3011: CVSLog
3012: .logMsg("ERROR failed reading Entries file from '"
3013: + newWorking.getPath()
3014: + "'");
3015:
3016: newEntries = new CVSEntryVector();
3017: }
3018:
3019: entry.setDirectoryEntryList(newEntries);
3020: }
3021: }
3022:
3023: isDir = false; // reset the directory flag.
3024: }
3025: }
3026:
3027: if (in != null) {
3028: try {
3029: in.close();
3030: } catch (IOException ex) {
3031: }
3032: }
3033:
3034: return entries;
3035: }
3036:
3037: public boolean writeAdminFiles() {
3038: boolean result = false;
3039:
3040: String localPath = this .getLocalRootDirectory();
3041:
3042: if (CVSProject.debugEntryIO)
3043: CVSTracer.traceIf(true,
3044: "CVSProject.writeAdminFiles: WRITE ADMIN FILES\n"
3045: + " localPath '" + localPath + "'\n"
3046: + " rootEntry "
3047: + this .rootEntry.dumpString());
3048:
3049: result = this .writeAdminAndDescend(localPath, this .rootEntry);
3050:
3051: if (!result) {
3052: // UNDONE - can we report better here?
3053: CVSLog
3054: .logMsg("CVSProject.writeAdminFiles:\n"
3055: + " ERROR Writing the CVS administrative files FAILED!");
3056: }
3057:
3058: return result;
3059: }
3060:
3061: private boolean writeAdminAndDescend(String localRoot,
3062: CVSEntry dirEntry) {
3063: int i;
3064: boolean result = true;
3065:
3066: String localDir = dirEntry.getLocalDirectory();
3067:
3068: String localPath = localRoot;
3069: if (localDir.length() > 2) {
3070: localPath = localPath + "/" + localDir.substring(2);
3071: }
3072:
3073: if (CVSProject.debugEntryIO)
3074: CVSTracer.traceIf(true,
3075: "CVSProject.writeAdminFiles: WRITE AND DESCEND LOCAL PATH\n"
3076: + " localPath '" + localPath + "'");
3077:
3078: String adminRootPath = CVSProject
3079: .rootPathToAdminPath(localPath);
3080:
3081: File adminFile = new File(CVSCUtilities
3082: .exportPath(adminRootPath));
3083:
3084: File rootFile = new File(CVSCUtilities.exportPath(CVSProject
3085: .getAdminRootPath(adminRootPath)));
3086:
3087: File reposFile = new File(CVSCUtilities.exportPath(CVSProject
3088: .getAdminRepositoryPath(adminRootPath)));
3089:
3090: File entriesFile = new File(CVSCUtilities.exportPath(CVSProject
3091: .getAdminEntriesPath(adminRootPath)));
3092:
3093: CVSEntryVector entries = dirEntry.getEntryList();
3094:
3095: if (CVSProject.debugEntryIO) {
3096: CVSTracer.traceIf(true,
3097: "===================================="
3098: + "====================================");
3099: CVSTracer.traceIf(true,
3100: "CVSProject.writeAdminAndDescend:\n"
3101: + " dirEntry '"
3102: + dirEntry.getFullName()
3103: + "'\n"
3104: + " isDirty '"
3105: + dirEntry.isDirty()
3106: + "'\n"
3107: + " dirRepos '"
3108: + dirEntry.getRepository()
3109: + "'\n"
3110: + " localRoot '"
3111: + localPath
3112: + "'\n"
3113: + " localDir '"
3114: + localDir
3115: + "'\n"
3116: + " adminFile '"
3117: + adminFile.getPath()
3118: + "'\n"
3119: + " rootFile '"
3120: + rootFile.getPath()
3121: + "'\n"
3122: + " reposFile '"
3123: + reposFile.getPath()
3124: + "'\n"
3125: + " entriesFile '"
3126: + entriesFile.getPath()
3127: + "'\n"
3128: + " entries.size '"
3129: + entries.size()
3130: + "'\n"
3131: + " entries.dirty '"
3132: + entries.isDirty() + "'");
3133: }
3134:
3135: if (!dirEntry.isDirty() && !entries.isDirty()) {
3136: if (CVSProject.debugEntryIO)
3137: CVSTracer.traceIf(true,
3138: "\nCVSProject.writeAdminAndDescend: "
3139: + "NO DIRTY ENTRIES --> SKIP WRITE\n");
3140: } else {
3141: if (!adminFile.exists()) {
3142: if (!adminFile.mkdir()) {
3143: CVSTracer
3144: .traceWithStack("ERROR could not create the admin directory '"
3145: + adminFile.getPath() + "'");
3146: }
3147: }
3148:
3149: // ============== ENTRIES ==================
3150: result = this .writeAdminEntriesFile(entriesFile, entries);
3151:
3152: if (result) {
3153: // ============== ROOT ==================
3154: String rootDirStr = this .projectDef
3155: .getRootDirectorySpec();
3156:
3157: if (CVSProject.debugEntryIO)
3158: CVSTracer.traceIf(true,
3159: "CVSProject.writeAdminAndDescend: WRITE ROOT FILE\n"
3160: + " rootFile '"
3161: + rootFile.getPath() + "'\n"
3162: + " " + rootDirStr);
3163:
3164: result = this .writeAdminRootFile(rootFile, rootDirStr);
3165:
3166: // ============== REPOSITORY ==================
3167: if (result) {
3168: if (CVSProject.debugEntryIO)
3169: CVSTracer.traceIf(true,
3170: "CVSProject.writeAdminAndDescend: WRITE REPOSITORYy FILE\n"
3171: + " reposFile '"
3172: + reposFile.getPath() + "'\n"
3173: + " "
3174: + dirEntry.getRepository());
3175:
3176: result = this .writeAdminRepositoryFile(reposFile,
3177: dirEntry.getRepository());
3178: }
3179: }
3180: }
3181:
3182: if (!result) {
3183: CVSLog.logMsg("CVSProject.writeAdminAndDescend: "
3184: + "ERROR writing admin files '"
3185: + entriesFile.getPath() + "' et.al.");
3186: result = false;
3187: }
3188:
3189: for (i = 0; result && i < entries.size(); ++i) {
3190: CVSTracer.traceIf(CVSProject.debugEntryIO,
3191: "CVSProject.writeAdminAndDescend: LOOP i = " + i);
3192:
3193: CVSEntry entry = entries.entryAt(i);
3194:
3195: CVSTracer.traceIf(CVSProject.debugEntryIO,
3196: "CVSProject.writeAdminAndDescend: " + "LOOP[" + i
3197: + "] repository '" + repository
3198: + "' entry '" + entry.getName() + "'");
3199:
3200: if (entry.isDirectory()) {
3201: // REVIEW I know this is gonna fail on subtrees!!!
3202: //
3203: CVSTracer.traceIf(CVSProject.debugEntryIO,
3204: "CVSProject.writeAdminAndDescend: "
3205: + "DESCEND into '"
3206: + entry.getFullName() + "'");
3207:
3208: result = this .writeAdminAndDescend(localRoot, entry);
3209:
3210: CVSTracer.traceIf(CVSProject.debugEntryIO,
3211: "CVSProject.writeAdminAndDescend: "
3212: + "RETURNED from '"
3213: + entry.getFullName() + "' with '"
3214: + result + "'");
3215: }
3216: }
3217:
3218: if (result) {
3219: entries.setDirty(false);
3220: dirEntry.setDirty(false);
3221: }
3222:
3223: CVSTracer
3224: .traceIf(CVSProject.debugEntryIO,
3225: "- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
3226: return result;
3227: }
3228:
3229: public boolean writeAdminEntriesFile(File entriesFile,
3230: CVSEntryVector entries) {
3231: boolean ok = true;
3232: boolean result = true;
3233: CVSEntry entry = null;
3234: BufferedWriter out = null;
3235:
3236: try {
3237: out = new BufferedWriter(new FileWriter(entriesFile));
3238: } catch (Exception ex) {
3239: CVSLog.logMsg("CVSProject.writeAdminEntriesFile: "
3240: + "ERROR opening entries file '"
3241: + entriesFile.getPath() + "' - " + ex.getMessage());
3242:
3243: return false;
3244: }
3245:
3246: for (int i = 0; result && i < entries.size(); ++i) {
3247: entry = entries.entryAt(i);
3248:
3249: try {
3250: out.write(entry.getAdminEntryLine());
3251: out.newLine();
3252: } catch (IOException ex) {
3253: CVSLog.logMsg("CVSProject.writeAdminEntriesFile: "
3254: + "ERROR writing entries file '"
3255: + entriesFile.getPath() + "' - "
3256: + ex.getMessage());
3257:
3258: result = false;
3259: }
3260: }
3261:
3262: try {
3263: out.close();
3264: } catch (IOException ex) {
3265: CVSLog.logMsg("CVSProject.writeAdminEntriesFile: "
3266: + "ERROR closing entries file '"
3267: + entriesFile.getPath() + "' - " + ex.getMessage());
3268:
3269: result = false;
3270: }
3271:
3272: return result;
3273: }
3274:
3275: public boolean writeAdminRootFile(File rootFile,
3276: String rootDirectoryStr) {
3277: boolean result = true;
3278: BufferedWriter out = null;
3279:
3280: try {
3281: out = new BufferedWriter(new FileWriter(rootFile));
3282: } catch (Exception ex) {
3283: CVSLog.logMsg("CVSProject.writeAdminRootFile: "
3284: + "failed opening 'Root' file to '"
3285: + rootFile.getPath() + "' - " + ex.getMessage());
3286: result = false;
3287: }
3288:
3289: if (result) {
3290: try {
3291: out.write(rootDirectoryStr);
3292: out.newLine();
3293: out.close();
3294: } catch (IOException ex) {
3295: CVSLog
3296: .logMsg("CVSProject.writeAdminRootFile: "
3297: + "failed writing 'Root' file to '"
3298: + rootFile.getPath() + "' - "
3299: + ex.getMessage());
3300: result = false;
3301: }
3302: }
3303:
3304: return result;
3305: }
3306:
3307: public boolean writeAdminRepositoryFile(File repFile,
3308: String repository) {
3309: boolean result = true;
3310: BufferedWriter out = null;
3311:
3312: if (CVSProject.deepDebug)
3313: CVSTracer.traceIf(true,
3314: "CVSProject.writeAdminRepositoryFile:\n"
3315: + " File: " + repFile.getPath() + "\n"
3316: + " Repos: " + repository);
3317:
3318: try {
3319: out = new BufferedWriter(new FileWriter(repFile));
3320: } catch (Exception ex) {
3321: CVSLog.logMsg("CVSProject.writeAdminRepositoryFile: "
3322: + "failed opening 'Repository' file to '"
3323: + repFile.getPath() + "' - " + ex.getMessage());
3324: result = false;
3325: }
3326:
3327: if (result) {
3328: try {
3329: out.write(repository);
3330: out.newLine();
3331: out.close();
3332: } catch (IOException ex) {
3333: CVSLog.logMsg("CVSProject.writeAdminRepositoryFile: "
3334: + "failed writing 'Repository' file to '"
3335: + repFile.getPath() + "' - " + ex.getMessage());
3336: result = false;
3337: }
3338: }
3339:
3340: return result;
3341: }
3342:
3343: public boolean isLocalFileModified(CVSEntry entry) {
3344: File entryFile = this .getEntryFile(entry);
3345: return entry.isLocalFileModified(entryFile);
3346: }
3347:
3348: /**
3349: * This is used for the 'release' command to determine
3350: * if the project has any modifications the user might
3351: * not want to lose.
3352: *
3353: * @return True if the project has any changes user might want to save.
3354: */
3355:
3356: public boolean checkReleaseStatus(CVSIgnore ignore, Vector mods,
3357: Vector adds, Vector rems, Vector unks) {
3358: //
3359: // NOTE
3360: // WARNING !!!
3361: //
3362: // THESE METHODS that operate on the localRootDirectory
3363: // MUST BE VERY CAREFUL!
3364: //
3365: // The problem is that the root entry of the project always
3366: // point to the very root of the project, even if the user
3367: // "sees" some subtree of the repository in question. When
3368: // the user is looking at a subtree, and they do a "release",
3369: // they do not expect the top level to be removed!!!!!!!!
3370: // They expect the "root level entries" to be released!
3371: //
3372: // Ergo, we must compute the local root directory for the
3373: // release based on both the localRootDirectory and the
3374: // local directory of one of the root level entries.
3375: //
3376: CVSEntryVector rootEntries = this .getRootEntry().getEntryList();
3377: if (rootEntries == null || rootEntries.size() == 0) {
3378: CVSTracer.traceWithStack("THIS SHOULD NEVER HAPPEN!!");
3379: return true;
3380: }
3381:
3382: this .checkReleaseAndDescend(this .getRootEntry(), ignore, mods,
3383: adds, rems, unks);
3384:
3385: return (mods.size() > 0 || adds.size() > 0 || rems.size() > 0 || unks
3386: .size() > 0);
3387: }
3388:
3389: private void checkReleaseAndDescend(CVSEntry parent,
3390: CVSIgnore ignore, Vector mods, Vector adds, Vector rems,
3391: Vector unks) {
3392: CVSEntryVector entries = parent.getEntryList();
3393:
3394: File dirF = this .getLocalEntryFile(parent);
3395: String[] list = dirF.list();
3396: Vector fileV = new Vector((list == null) ? 0 : list.length);
3397:
3398: if (list != null) {
3399: for (int i = 0; i < list.length; ++i)
3400: fileV.addElement(list[i]);
3401: }
3402:
3403: for (int i = 0; i < entries.size(); ++i) {
3404: CVSEntry entry = entries.entryAt(i);
3405:
3406: // Anything we have an entry for is not unknown
3407: fileV.removeElement(entry.getName());
3408:
3409: if (entry.isDirectory()) {
3410: this .checkReleaseAndDescend(entry, ignore, mods, adds,
3411: rems, unks);
3412: } else {
3413: if (entry.isNewUserFile())
3414: adds.addElement(entry.getFullName());
3415: else if (entry.isToBeRemoved())
3416: rems.addElement(entry.getFullName());
3417: else if (entry.isInConflict())
3418: mods.addElement(entry.getFullName());
3419: else if (this .isLocalFileModified(entry))
3420: mods.addElement(entry.getFullName());
3421: else if (this .isLocalFileModified(entry))
3422: mods.addElement(entry.getFullName());
3423: }
3424: }
3425:
3426: for (int i = 0, sz = fileV.size(); i < sz; ++i) {
3427: String fileName = (String) fileV.elementAt(i);
3428: if (!ignore.isFileToBeIgnored(fileName)) {
3429: // parent is a dir entry, which always has a '/'
3430: // on the end of its fullname.
3431: unks.addElement(parent.getFullName() + fileName);
3432: }
3433: }
3434: }
3435:
3436: public void pruneEmptySubDirs(boolean saveAdminFiles) {
3437: this .pruneEmptySubDirs(this .getRootEntry());
3438:
3439: if (saveAdminFiles) {
3440: this .writeAdminFiles();
3441: }
3442: }
3443:
3444: public void pruneEmptySubDirs(CVSEntry parent) {
3445: CVSEntryVector entries = parent.getEntryList();
3446: for (int i = entries.size() - 1; i >= 0; --i) {
3447: CVSEntry entry = entries.getEntryAt(i);
3448: if (entry.isDirectory()) {
3449: File dirF = this .getEntryFile(entry);
3450: String[] list = dirF.list();
3451: // MTT-null-list
3452: if (list == null || list.length == 0) {
3453: this .descendAndDelete(dirF);
3454: parent.removeEntry(entry);
3455: } else if (list.length == 1 && list[0].equals("CVS")) {
3456: File cvsF = new File(dirF, "CVS");
3457: if ((!cvsF.exists()) || cvsF.isDirectory()) {
3458: this .descendAndDelete(dirF);
3459: parent.removeEntry(entry);
3460: }
3461: } else {
3462: this .pruneEmptySubDirs(entry);
3463: }
3464: }
3465: }
3466: }
3467:
3468: public void releaseProject() {
3469: //
3470: // NOTE
3471: // WARNING !!!
3472: //
3473: // THESE METHODS that operate on the localRootDirectory
3474: // MUST BE VERY CAREFUL!
3475: //
3476: // The problem is that the root entry of the project always
3477: // point to the very root of the project, even if the user
3478: // "sees" some subtree of the repository in question. When
3479: // the user is looking at a subtree, and they do a "release",
3480: // they do not expect the top level to be removed!!!!!!!!
3481: // They expect the "root level entries" to be released!
3482: //
3483: // Ergo, we must compute the local root directory for the
3484: // release based on both the localRootDirectory and the
3485: // local directory of one of the root level entries.
3486: //
3487: CVSEntryVector rootEntries = this .getRootEntry().getEntryList();
3488: if (rootEntries == null || rootEntries.size() == 0) {
3489: CVSTracer.traceWithStack("THIS SHOULD NEVER HAPPEN!!");
3490: return;
3491: }
3492:
3493: for (int i = rootEntries.size() - 1; i >= 0; --i) {
3494: CVSEntry entry = rootEntries.getEntryAt(i);
3495:
3496: File eFile = this .getEntryFile(entry);
3497:
3498: //
3499: // SANITY CHECK
3500: // To at least make CERTAIN that we never delete anythig
3501: // above the localRootDirectory...
3502: //
3503:
3504: if (!CVSCUtilities.isSubpathInPath(this .getLocalRootPath(),
3505: eFile.getPath())) {
3506: String msg = "ROOT '" + this .getLocalRootPath()
3507: + "' NOT parent of '" + eFile.getPath() + "'";
3508: CVSTracer.traceWithStack(msg);
3509: return;
3510: }
3511:
3512: this .descendAndDelete(eFile);
3513: }
3514:
3515: // UNDONE We need to adjust the "top level Entries" here...
3516:
3517: File rootDir = new File(this .getLocalRootDirectory());
3518: if (rootDir.exists()) {
3519: String[] files = rootDir.list();
3520: if (files == null || files.length < 2) {
3521: // NOTE
3522: // This is very important! As long as we have
3523: // the top level CVS admin directory, it is critical
3524: // that we only delete it if it is the ONLY file
3525: // left at the top level!
3526: //
3527: // UNTIL, that is, we fix the above UNDONE wrt Entries.
3528: //
3529: boolean doit = true;
3530: if (files.length == 1) {
3531: if (files[0].equals("CVS")) {
3532: File cvsDir = new File(rootDir, "CVS");
3533:
3534: files = cvsDir.list();
3535: for (int c = 0; c < files.length; ++c) {
3536: File dFile = new File(cvsDir, files[c]);
3537: dFile.delete();
3538: }
3539:
3540: cvsDir.delete();
3541: } else {
3542: doit = false;
3543: }
3544: }
3545:
3546: if (doit) {
3547: rootDir.delete();
3548: }
3549: }
3550: }
3551: }
3552:
3553: private void descendAndDelete(File eFile) {
3554: if (eFile.isDirectory()) {
3555: String[] files = eFile.list();
3556:
3557: if (files != null) {
3558: for (int i = 0; i < files.length; ++i) {
3559: File f = new File(eFile, files[i]);
3560: if (f.exists()) {
3561: this .descendAndDelete(f);
3562: }
3563: }
3564: }
3565: }
3566:
3567: eFile.delete();
3568: }
3569:
3570: //
3571: // This is tricky.
3572: // We have to go back to the project's entries list
3573: // to make the check, since the entry sent from the
3574: // server will not reflect the local timestamp!
3575: //
3576: public boolean checkOverwrite(CVSEntry entry, File file) {
3577: // UNDONE
3578: // The current algorithm is very weak.
3579: boolean result = true;
3580:
3581: if (CVSProject.deepDebug)
3582: CVSTracer.traceIf(true, "CVSProject.checkOverWrite( "
3583: + entry.getFullName() + ", " + file.getPath()
3584: + " )");
3585:
3586: if (!file.exists()) {
3587: // Does not exist, no problem overwriting...
3588: CVSTracer.trace("CVSProject.checkOverWrite: FILE '"
3589: + file.getPath() + "' DOES NOT EXIST");
3590: return true;
3591: }
3592:
3593: CVSEntry checkEntry = this .locateEntry(entry.getFullName());
3594:
3595: if (checkEntry == null) {
3596: NullPointerException ex = new NullPointerException(
3597: "locateEntry(" + entry.getFullName()
3598: + ") returns null!");
3599: CVSLog.traceMsg(ex, "CVSProject.checkOverWrite:");
3600: return false;
3601: }
3602:
3603: // Check the timstamps...
3604: result = !checkEntry.isLocalFileModified(file);
3605:
3606: CVSTracer.traceIf(false, "CVSProject.checkOverWrite: RESULT '"
3607: + result + "'");
3608:
3609: return result;
3610: }
3611:
3612: public CVSEntry locateEntry(String fullPath) {
3613: CVSEntry entry = null;
3614:
3615: if (CVSProject.deepDebug)
3616: CVSTracer.traceIf(true, "CVSProject.locateEntry( "
3617: + fullPath + " )");
3618:
3619: int index = fullPath.lastIndexOf('/');
3620: if (index < 0) {
3621: CVSTracer
3622: .traceWithStack("CVSProject.locateEntry: NO SLASH IN '"
3623: + fullPath + "'");
3624: entry = this .rootEntry.locateEntry(fullPath);
3625: } else {
3626: String name = fullPath.substring(index + 1);
3627: String localDirectory = fullPath.substring(0, index + 1);
3628:
3629: CVSEntry parentEntry = this
3630: .getPathTableEntry(localDirectory);
3631:
3632: if (parentEntry == null) {
3633: CVSTracer
3634: .traceWithStack("CVSProject.locateEntry: LOCAL DIRECTORY '"
3635: + localDirectory + "' NOT IN TABLE");
3636: } else {
3637: if (false)
3638: CVSTracer.traceIf(false,
3639: "CVSProject.locateEntry: PARENT '"
3640: + parentEntry.getFullName() + "'");
3641:
3642: entry = parentEntry.locateEntry(name);
3643: }
3644: }
3645:
3646: if (CVSProject.deepDebug)
3647: CVSTracer.traceIf(true,
3648: "CVSProject.locateEntry: fullPath '"
3649: + fullPath
3650: + "' resulting entry '"
3651: + (entry == null ? "(null)" : entry
3652: .getFullName()));
3653:
3654: return entry;
3655: }
3656:
3657: public boolean ensureEntryHierarchy(String localDirectory,
3658: String repository) {
3659: if (CVSProject.deepDebug)
3660: CVSTracer.traceIf(true,
3661: "CVSProject.ENSUREEntryHierarchy:\n"
3662: + " localDirectory '" + localDirectory
3663: + "'\n" + " repository '" + repository);
3664:
3665: if (localDirectory.equals("./")) {
3666: if (this .rootEntry == null) {
3667: if (CVSProject.deepDebug)
3668: CVSTracer.traceIf(true,
3669: "CVSProject.ENSUREEntryHierarchy: ESTABLISH '.' ROOT ENTRY\n"
3670: + " repository '" + repository);
3671:
3672: this .establishRootEntry(repository);
3673:
3674: return true;
3675: } else {
3676: // REVIEW
3677: // Should this verify the 'repository' exists?!
3678: // Given the note below, I do not think there is
3679: // any methd that would properly parse this case.
3680: // Ergo, we hope it can only occur if the entry
3681: // already exists.
3682: //
3683: CVSTracer.traceIf(true,
3684: "CVSProject.ENSUREEntryHierarchy: IGNORING '.' localDirectory!\n"
3685: + " repository '" + repository);
3686: return true;
3687: }
3688: }
3689:
3690: CVSEntry lookupEntry = this .getPathTableEntry(localDirectory);
3691:
3692: // The local directory is the Path Table.
3693: // Thus, the entry must already exist, return.
3694: //
3695: if (lookupEntry != null)
3696: return true;
3697:
3698: //
3699: // We are going to get response items that sometimes have no
3700: // corresponding entry in the Path Table. Further, these entries
3701: // have no corresponding hierarchy in the CVSEntry tree, which
3702: // in turn implies no nodes in the EntryTree of jCVS.
3703: //
3704: // This code will attempt to parse the incoming item and ensure
3705: // that the CVSEntry's in the item's path exist.
3706: //
3707: // We have updated this code to make a HUGE NEW ASSUMPTION!!!!!
3708: //
3709: // We are assuming that we will never see a request to ensure a
3710: // hierarchy in which the parent nodes do not exist. In other
3711: // words, we should never see a case where the 'local directory'
3712: // of the item contains more than one subdirectory beyond what
3713: // is "currently ensured". In other words, items will not be
3714: // seen for a subdirectory that is more than one level below
3715: // a level we already have ensured.
3716: //
3717: // If that assumption is wrong, we are going to have to make
3718: // another assumption that the only time these paths would be
3719: // out of sync is when a module alias is used. In these cases,
3720: // the paths MUST be in sync when working back from the end.
3721: // I can not think of a case in which this would not be true.
3722: //
3723: // Example: ( module alias "api api/machdep/linux" )
3724: //
3725: // Entry LocalDir Repository
3726: // ROOT ./ /cvs/
3727: // api ./api/ /cvs/api/machdep/linux
3728: // include ./api/include/ /cvs/api/machdep/linux/include
3729: //
3730: // In that example, if we got 'include' before we got 'api', we would have
3731: // to work back from include up to './', and assume that the top level entry
3732: // subsumes the remainder of the repository path as its own, since only
3733: // aliases should have this case, and aliases can only apply to the top level.
3734: //
3735:
3736: if (CVSProject.deepDebug)
3737: CVSTracer.traceIf(true,
3738: "CVSProject.ensureEntryHierarchy: START LOOP\n"
3739: + " localDirectory '" + localDirectory
3740: + "'\n" + " repository '"
3741: + repository + "'");
3742:
3743: CVSEntry curEntry = this .rootEntry;
3744:
3745: // FIRST, we work FORWARD eliminating as much of the path as
3746: // we can find in the Path Table.
3747: //
3748: for (;;) {
3749: int length = curEntry.getLocalDirectory().length();
3750: int slashIdx = localDirectory.indexOf("/", length);
3751:
3752: if (CVSProject.deepDebug)
3753: CVSTracer.traceIf(true,
3754: "CVSProject.ensureEntryHierarchy: TOP LOOP\n"
3755: + " length = " + length
3756: + " slashIdx = " + slashIdx + "\n"
3757: + " curEntry: "
3758: + curEntry.dumpString(" "));
3759:
3760: if (slashIdx == -1)
3761: break;
3762:
3763: String subLocal = localDirectory.substring(0, slashIdx + 1);
3764: CVSEntry pathEntry = this .getPathTableEntry(subLocal);
3765:
3766: if (CVSProject.deepDebug)
3767: CVSTracer.traceIf(true,
3768: "CVSProject.ensureEntryHierarchy: "
3769: + "LOOP lookup path '"
3770: + subLocal
3771: + "' returns:\n"
3772: + (pathEntry == null ? "NULL"
3773: : pathEntry.dumpString(" ")));
3774:
3775: if (pathEntry == null)
3776: break;
3777:
3778: curEntry = pathEntry;
3779: }
3780: /*
3781: index slashCnt = CVSCUtilities.getSlashCount( subLocal );
3782: if ( slashCnt == 1 )
3783: {
3784: // GREAT! We have a simple one level entry, which is simple!
3785: this.pathTable.put( localDirectory, repository );
3786:
3787: String name =
3788: CVSCUtilities.stripFinalSlash
3789: ( localDirectory.substring
3790: ( curEntry.getRepository().length() ) );
3791:
3792: if ( CVSProject.deepDebug )
3793: CVSTracer.traceIf( true,
3794: "CVSProject.ensureEntryHierarchy: ADD NEW ENTRY:\n"
3795: + " name '" + name + "\n"
3796: + " repository '" + repository + "'\n"
3797: + " localdir '" + localDirectory + "'" );
3798:
3799: CVSEntry newEntry = new CVSEntry();
3800: newEntry.setName( name );
3801: newEntry.setRepository( repository );
3802: newEntry.setLocalDirectory( localDirectory );
3803: newEntry.setVersion( "" );
3804: newEntry.setTimestamp( "" );
3805: newEntry.setOptions( "" );
3806: newEntry.setTag( "" );
3807: newEntry.setDirty( true );
3808:
3809: // NOTE setDirectoryEntryList() sets 'isDir'-ness of entry.
3810: newEntry.setDirectoryEntryList( new CVSEntryVector() );
3811:
3812: if ( CVSProject.deepDebug )
3813: CVSTracer.traceIf( true,
3814: "CVSProject.ensureEntryHierarchy: NEW ENTRY =\n"
3815: + newEntry.dumpSting( " " ) );
3816:
3817: curEntry.appendEntry( newEntry );
3818: return true;
3819: }
3820: */
3821: //
3822: // Ok. We now have a 'local directory' subpath that does not exist
3823: // yet. We will work backwards building the elements in the path,
3824: // into a Vector. Then, we will roll then out forwards.
3825: //
3826: Vector elements = new Vector();
3827:
3828: String workingRepos = CVSCUtilities
3829: .ensureFinalSlash(repository);
3830:
3831: int reposIdx = workingRepos.length() - 1;
3832: int localIdx = localDirectory.length() - 1;
3833:
3834: if (CVSProject.deepDebug)
3835: CVSTracer.traceIf(true,
3836: "CVSProject.ensureEntryHierarchy: START MULTI LEVEL LOOP\n"
3837: + " reposIdx = " + reposIdx
3838: + " localIdx = " + localIdx + "\n"
3839: + " localDirectory = '" + localDirectory
3840: + "'\n" + " workingRepos = '"
3841: + workingRepos + "'\n"
3842: + " curEntry = \n"
3843: + curEntry.dumpString(" "));
3844:
3845: for (;;) {
3846: int newRepIdx = workingRepos.lastIndexOf("/", reposIdx - 1);
3847: int newLocIdx = localDirectory.lastIndexOf("/",
3848: localIdx - 1);
3849:
3850: if (CVSProject.deepDebug)
3851: CVSTracer.traceIf(true,
3852: "CVSProject.ensureEntryHierarchy: PARSE PATH LOOP\n"
3853: + " reposIdx = " + reposIdx
3854: + " localIdx = " + localIdx + "\n"
3855: + " newRepIdx = " + newRepIdx
3856: + " newLocIdx = " + newLocIdx);
3857:
3858: String name = localDirectory.substring(newLocIdx + 1,
3859: localIdx);
3860: String subRepos = workingRepos.substring(0, reposIdx); // drop final slash
3861: String subLocal = localDirectory.substring(0, localIdx + 1); // include final slash
3862:
3863: if (CVSProject.deepDebug)
3864: CVSTracer.traceIf(true,
3865: "CVSProject.ensureEntryHierarchy: CHECK PATH\n"
3866: + " name = '" + name + "'\n"
3867: + " subRepos = '" + subRepos + "'\n"
3868: + " subLocal = '" + subLocal + "'");
3869:
3870: if (subLocal.equals(curEntry.getLocalDirectory())) {
3871: if (CVSProject.deepDebug)
3872: CVSTracer
3873: .traceIf(true,
3874: "CVSProject.ensureEntryHierarchy: HIT CUR-ENTRY, BREAK");
3875: break;
3876: }
3877:
3878: if (CVSProject.deepDebug)
3879: CVSTracer.traceIf(true,
3880: "CVSProject.ensureEntryHierarchy: ADDED PATH!");
3881:
3882: String[] parms = { name, subRepos, subLocal };
3883: elements.addElement(parms);
3884:
3885: reposIdx = newRepIdx;
3886: localIdx = newLocIdx;
3887: }
3888:
3889: for (int i = elements.size() - 1; i >= 0; --i) {
3890: String[] parms = (String[]) elements.elementAt(i);
3891:
3892: CVSEntry newEntry = new CVSEntry();
3893: newEntry.setDirty(true);
3894: newEntry.setName(parms[0]);
3895: newEntry.setRepository(parms[1]);
3896: newEntry.setLocalDirectory(parms[2]);
3897: newEntry.setVersion("");
3898: newEntry.setTimestamp("");
3899: newEntry.setOptions("");
3900: newEntry.setTag("");
3901:
3902: // NOTE setDirectoryEntryList() sets 'isDir'-ness of entry.
3903: // If we do not do this, the the getFullPathName() used
3904: // to get the working directory will include the dir's
3905: // name in the path, which is redundant and throws off
3906: // the algorithm by a directory level!
3907: //
3908: newEntry.setDirectoryEntryList(new CVSEntryVector());
3909:
3910: //
3911: // NOTE
3912: //
3913: // If there is an existing Entries file in place, then we have
3914: // the case where we are picking up something that is laying on
3915: // top of an existing working directory. This is almost always
3916: // the case of the user performing a checkout of another module
3917: // on top of an existing working directory. In this case, we
3918: // must read in the existing Entries file, or we will end up
3919: // over-writing the file when we save the current hierarchy!
3920: //
3921: File workingDirF = new File(CVSCUtilities.exportPath(this
3922: .getLocalRootDirectory()));
3923:
3924: workingDirF = new File(workingDirF, CVSCUtilities
3925: .exportPath(newEntry.getFullPathName()));
3926:
3927: CVSEntryVector entries = this .readEntriesFile(newEntry,
3928: workingDirF);
3929:
3930: if (CVSProject.deepDebug)
3931: CVSTracer
3932: .traceIf(true,
3933: "CVSProject.ensureEntryHierarchy: "
3934: + "Entries = "
3935: + entries
3936: + ", size="
3937: + (entries == null ? 0
3938: : entries.size())
3939: + ", at '"
3940: + workingDirF.getAbsolutePath()
3941: + "'");
3942:
3943: if (entries != null) {
3944: newEntry.setDirectoryEntryList(entries);
3945: }
3946:
3947: if (CVSProject.deepDebug)
3948: CVSTracer.traceIf(true,
3949: "CVSProject.ensureEntryHierarchy: "
3950: + "MULTI LEVEL APPEND NEW ENTRY\n"
3951: + " CUR ENTRY:"
3952: + curEntry.dumpString(" ") + "\n"
3953: + " NEW ENTRY:"
3954: + newEntry.dumpString(" "));
3955:
3956: this .pathTable.put(newEntry.getLocalDirectory(), newEntry);
3957:
3958: curEntry.appendEntry(newEntry);
3959: curEntry = newEntry;
3960: }
3961:
3962: return true;
3963: }
3964:
3965: // subpath is the local path up to the name.
3966: public boolean ensureProperWorkingDirectory(String localRoot,
3967: String subPath, boolean ensureAdmin) {
3968: int index;
3969: boolean result = true;
3970:
3971: if (CVSProject.deepDebug)
3972: CVSTracer.traceIf(true,
3973: "CVSClient.ENSURE Proper WORKING Directory:\n"
3974: + " localRoot '" + localRoot + "'\n"
3975: + " subPath '" + subPath + "'\n"
3976: + " ensureAdm '" + ensureAdmin + "'");
3977:
3978: subPath = CVSCUtilities.stripFinalSlash(subPath);
3979: if (subPath.startsWith("./"))
3980: subPath = subPath.substring(2);
3981:
3982: String remainder = subPath;
3983:
3984: for (; result && remainder.length() > 0;) {
3985: index = remainder.indexOf('/');
3986: if (index < 0) {
3987: subPath = remainder;
3988: remainder = "";
3989: } else {
3990: subPath = remainder.substring(0, index);
3991: remainder = remainder.substring(index + 1);
3992: }
3993:
3994: // UNDONE separator
3995: File dir = new File(localRoot + "/" + subPath);
3996:
3997: if (!dir.exists())
3998: dir.mkdir();
3999:
4000: if (!dir.exists()) {
4001: result = false;
4002: CVSLog.logMsg("ERROR could not create local path '"
4003: + dir.getPath() + "'");
4004: } else if (!dir.isDirectory()) {
4005: result = false;
4006: CVSLog.logMsg("ERROR local directory '" + dir.getPath()
4007: + "' is not a directory!");
4008: } else if (result && ensureAdmin) {
4009: File adminDir = // UNDONE separator
4010: new File(dir.getPath() + "/CVS");
4011:
4012: if (CVSProject.deepDebug)
4013: CVSTracer.traceIf(true,
4014: "CVSClient.ensureProperWorkingDirectory: ADMINDIR '"
4015: + adminDir.getPath() + "'");
4016:
4017: if (!adminDir.exists())
4018: adminDir.mkdir();
4019:
4020: if (!adminDir.exists()) {
4021: result = false;
4022: CVSLog.logMsg("ERROR could not create Admin path '"
4023: + this .localAdminDirFile.getPath() + "'");
4024: }
4025: }
4026:
4027: // UNDONE separator
4028: localRoot = localRoot + "/" + subPath;
4029: }
4030:
4031: return result;
4032: }
4033:
4034: public boolean ensureLocalTree(File localFile, boolean ensureAdmin) {
4035: int index;
4036: boolean result = true;
4037:
4038: String localPath = localFile.getPath();
4039:
4040: String localRoot = CVSCUtilities
4041: .exportPath(this .localRootDirectory);
4042:
4043: index = localPath.lastIndexOf(File.separatorChar);
4044: if (index < 0) {
4045: return true;
4046: }
4047:
4048: String localSub = localPath.substring(0, index);
4049:
4050: if (!CVSCUtilities.isSubpathInPath(localRoot, localSub)) {
4051: CVSLog
4052: .logMsg("CVSClient.ensureLocalTree: LOCAL SUBDIR IS NOT IN ROOT!!\n"
4053: + " localRoot '"
4054: + localRootDirectory
4055: + "'\n"
4056: + " localSubDir '"
4057: + localSub
4058: + "'");
4059: result = false;
4060: }
4061:
4062: // REVIEW
4063: //
4064: // Per Thorsten Ludewig <T.Ludewig@FH-Wolfenbuettel.DE>, who
4065: // reported that this eliminated an index out of bounds exception,
4066: // which I think other users were reporting also.
4067: //
4068: // Need to review to make sure that "./" is the correct substitution.
4069: //
4070: if (localSub.equals(localRoot)) {
4071: localSub = "./";
4072: } else {
4073: localSub = localSub.substring(localRoot.length() + 1);
4074: }
4075:
4076: if (CVSProject.deepDebug)
4077: CVSTracer.traceIf(true,
4078: "CVSClient.ensureLocalTree: tempFile '"
4079: + localFile.getPath() + "' localPath '"
4080: + localPath + "' --> '" + localSub + "'");
4081:
4082: result = this .ensureProperWorkingDirectory(
4083: this .localRootDirectory, localSub, ensureAdmin);
4084:
4085: return result;
4086: }
4087:
4088: public void moveLocalFile(File localFile, String versionStr)
4089: throws CVSFileException {
4090: //
4091: // REVIEW
4092: // UNDONE
4093: // Should we capitualate and use the 'standard' notation
4094: // of '.#name.version' (e.g., '.#main.c.1.4')? I prefer
4095: // this naming scheme (e.g., '#main.c.1.4').
4096: //
4097: String localPath = localFile.getPath();
4098:
4099: String base = "";
4100: String name = localPath;
4101: int index = localPath.lastIndexOf('/');
4102: if (index >= 0) {
4103: base = localPath.substring(0, index);
4104: name = localPath.substring(index + 1);
4105: }
4106:
4107: String newPath = base + "/" + "#" + name + "." + versionStr;
4108:
4109: CVSTracer.traceIf(CVSProject.overTraceProcessing,
4110: "CVSProject.moveLocalFile: move '"
4111: + localFile.getPath() + "' to '" + newPath
4112: + "'");
4113:
4114: File toFile = new File(newPath);
4115:
4116: boolean result = localFile.renameTo(toFile);
4117:
4118: CVSTracer.traceIf(false,
4119: "CVSProject.moveLocalFile: rename returns '" + result
4120: + "'");
4121:
4122: if (!result)
4123: throw new CVSFileException("failed renaming '"
4124: + localFile.getPath() + "' to '" + toFile.getPath()
4125: + "'");
4126: }
4127:
4128: public boolean updateLocalFile(CVSResponseItem item,
4129: CVSEntry entry, File localFile) {
4130: boolean result = true;
4131:
4132: int trans = CVSCUtilities.computeTranslation(entry);
4133:
4134: result = this .copyFile(item.getFile(), localFile, trans, item
4135: .isGZIPed());
4136:
4137: return result;
4138: }
4139:
4140: public boolean copyFile(File from, File to, int translation,
4141: boolean isGZIPed) {
4142: boolean result = false;
4143:
4144: CVSTracer
4145: .traceIf(
4146: CVSProject.overTraceProcessing,
4147: "CVSProject.copyFile: from '"
4148: + from.getPath()
4149: + "' to '"
4150: + to.getPath()
4151: + "' trans '"
4152: + (translation == CVSClient.TRANSLATE_ASCII ? "ASCII"
4153: : "NONE") + "' gzip-ed? '"
4154: + isGZIPed + "'");
4155:
4156: switch (translation) {
4157: case CVSClient.TRANSLATE_ASCII:
4158: result = this .copyFileAscii(from, to, isGZIPed);
4159: break;
4160:
4161: case CVSClient.TRANSLATE_NONE:
4162: default:
4163: result = this .copyFileRaw(from, to, isGZIPed);
4164: break;
4165: }
4166:
4167: return result;
4168: }
4169:
4170: public boolean copyFileAscii(File from, File to, boolean isGZIPed) {
4171: boolean ok = true;
4172:
4173: BufferedReader in = null;
4174: BufferedWriter out = null;
4175:
4176: String line = null;
4177:
4178: try {
4179: if (isGZIPed) {
4180: in = this .new NewLineReader(new InputStreamReader(
4181: new GZIPInputStream(new FileInputStream(from))));
4182: } else {
4183: in = new NewLineReader(new FileReader(from));
4184: }
4185: } catch (IOException ex) {
4186: in = null;
4187: ok = false;
4188: CVSLog
4189: .logMsg("CVSProject.copyFileAscii: failed creating in reader: "
4190: + ex.getMessage());
4191: }
4192:
4193: if (ok)
4194: try {
4195: out = new BufferedWriter(new FileWriter(to));
4196: } catch (IOException ex) {
4197: out = null;
4198: ok = false;
4199: CVSLog
4200: .logMsg("CVSProject.copyFileAscii: failed creating out writer: "
4201: + ex.getMessage());
4202: }
4203:
4204: if (out == null || in == null) {
4205: ok = false;
4206: CVSLog.logMsg("CVSProject.copyFileAscii: failed creating '"
4207: + (out == null ? "output writer" : "input reader")
4208: + "'");
4209: }
4210:
4211: if (ok) {
4212: for (;;) {
4213: try {
4214: line = in.readLine();
4215:
4216: if (line == null)
4217: break;
4218:
4219: out.write(line);
4220:
4221: // NB-eol
4222: // readLine now returns the EOL also. Stops cvsc from
4223: // adding EOL to binaries checked in as ascii and files
4224: // without eol at eof.
4225: //
4226: // out.newLine();
4227: } catch (IOException ex) {
4228: CVSLog
4229: .logMsg("CVSProject.copyFileAscii: failed during copy: "
4230: + ex.getMessage());
4231: ok = false;
4232: break;
4233: }
4234: }
4235:
4236: try {
4237: out.flush();
4238: } catch (IOException ex) {
4239: CVSLog
4240: .logMsg("CVSProject.copyFileAscii: failed flushing output: "
4241: + ex.getMessage());
4242: ok = false;
4243: }
4244: }
4245:
4246: try {
4247: if (in != null)
4248: in.close();
4249: if (out != null)
4250: out.close();
4251: } catch (IOException ex) {
4252: CVSLog
4253: .logMsg("CVSProject.copyFileAscii: failed closing files: "
4254: + ex.getMessage());
4255: ok = false;
4256: }
4257:
4258: return ok;
4259: }
4260:
4261: public boolean copyFileRaw(File from, File to, boolean isGZIPed) {
4262: int bytes;
4263: long fileSize;
4264: boolean ok = true;
4265:
4266: BufferedInputStream in = null;
4267: BufferedOutputStream out = null;
4268:
4269: String line = null;
4270:
4271: try {
4272: if (isGZIPed) {
4273: in = new BufferedInputStream(new GZIPInputStream(
4274: new FileInputStream(from)));
4275: } else {
4276: in = new BufferedInputStream(new FileInputStream(from));
4277: }
4278: } catch (Exception ex) {
4279: in = null;
4280: ok = false;
4281: CVSLog
4282: .logMsg("CVSProject.copyFileRaw: failed creating in reader: "
4283: + ex.getMessage());
4284: }
4285:
4286: if (ok) {
4287: try {
4288: out = new BufferedOutputStream(new FileOutputStream(to));
4289: } catch (Exception ex) {
4290: out = null;
4291: ok = false;
4292: CVSLog
4293: .logMsg("CVSProject.copyFileRaw: failed creating out writer: "
4294: + ex.getMessage());
4295: }
4296: }
4297:
4298: if (out == null || in == null) {
4299: ok = false;
4300: CVSLog.logMsg("CVSProject.copyFileRaw: failed creating '"
4301: + (out == null ? "output writer" : "input reader")
4302: + "'");
4303: }
4304:
4305: if (ok) {
4306: byte[] buffer;
4307: buffer = new byte[8192];
4308:
4309: fileSize = from.length();
4310: for (;;) {
4311: try {
4312: bytes = in.read(buffer, 0, 8192);
4313: } catch (IOException ex) {
4314: ok = false;
4315: CVSLog.logMsg("CVSProject.copyFileRaw: "
4316: + "ERROR reading file data:\n "
4317: + ex.getMessage());
4318: break;
4319: }
4320:
4321: if (bytes < 0)
4322: break;
4323:
4324: try {
4325: out.write(buffer, 0, bytes);
4326: } catch (IOException ex) {
4327: ok = false;
4328: CVSLog.logMsg("CVSProject.copyFileRaw: "
4329: + "ERROR writing output file:\n "
4330: + ex.getMessage());
4331: break;
4332: }
4333: }
4334: }
4335:
4336: try {
4337: if (in != null)
4338: in.close();
4339: if (out != null)
4340: out.close();
4341: } catch (IOException ex) {
4342: CVSLog
4343: .logMsg("CVSProject.copyFileRaw: failed closing files: "
4344: + ex.getMessage());
4345: ok = false;
4346: }
4347:
4348: return ok;
4349: }
4350:
4351: private class NewLineReader extends BufferedReader {
4352: public NewLineReader(Reader in) {
4353: super (in);
4354: }
4355:
4356: public String readLine() {
4357: char ch;
4358: StringBuffer line = new StringBuffer(132);
4359:
4360: try {
4361: for (;;) {
4362: int inByte = this .read();
4363: if (inByte == -1) {
4364: if (line.length() == 0)
4365: line = null;
4366: break;
4367: }
4368:
4369: ch = (char) inByte;
4370: if (ch == 0x0A) {
4371: // mtt - 21.02.02
4372: // we need to append the correct line end
4373: // for the platform we are running under.
4374: //
4375: // NB-eol
4376: // we need to not wrongly assume later
4377: // the line always ends in eol.
4378: //
4379: line.append(System
4380: .getProperty("line.separator"));
4381: break;
4382: }
4383:
4384: line.append(ch);
4385: }
4386: } catch (IOException ex) {
4387: line = null;
4388: }
4389:
4390: return (line != null ? line.toString() : null);
4391: }
4392: }
4393:
4394: //
4395: // CVS USER INTERFACE METHODS
4396: //
4397:
4398: // Currently, we stub these out.
4399: public void uiDisplayProgressMsg(String message) {
4400: }
4401:
4402: public void uiDisplayProgramError(String error) {
4403: }
4404:
4405: public void uiDisplayResponse(CVSResponse response) {
4406: }
4407:
4408: //
4409: // END OF CVS USER INTERFACE METHODS
4410: //
4411:
4412: public String toString() {
4413: return "CVSProject: name '" + this .repository + "'";
4414: }
4415:
4416: public StringBuffer dumpCVSProject(StringBuffer buf,
4417: String description) {
4418: buf
4419: .append("##############################################################\n");
4420: buf.append("#\n");
4421: buf.append("# CVSProject '").append(this .repository).append(
4422: "'\n");
4423: buf.append("#\n");
4424: buf.append("# Description '").append(description).append("'\n");
4425: buf.append("#\n");
4426: buf
4427: .append("##############################################################\n");
4428:
4429: buf.append("\n");
4430:
4431: buf.append(" valid? '").append(this .valid).append("'\n");
4432: buf.append(" isPServer? '").append(this .isPServer).append(
4433: "'\n");
4434: buf.append(" allowsGzip? '").append(this .allowGzipFileMode)
4435: .append("'\n");
4436: buf.append(" gzipLevel '").append(this .gzipStreamLevel)
4437: .append("'\n");
4438: buf.append(" connMethod '").append(this .connMethod).append(
4439: "'\n");
4440: buf.append(" serverCmd '").append(this .serverCommand)
4441: .append("'\n");
4442: buf.append(" rshProcess '").append(this .rshProcess).append(
4443: "'\n");
4444: buf.append(" userName '").append(this .userName).append(
4445: "'\n");
4446: buf.append(" tempPath '").append(this .tempPath).append(
4447: "'\n");
4448: buf.append(" repository '").append(this .repository).append(
4449: "'\n");
4450: buf.append(" rootDir '").append(this .rootDirectory)
4451: .append("'\n");
4452: buf.append(" localRoot '").append(this .localRootDirectory)
4453: .append("'\n");
4454:
4455: buf.append("\n");
4456:
4457: buf.append("------- Path Table -------\n");
4458:
4459: buf.append("\n");
4460:
4461: Enumeration numer = this .pathTable.keys();
4462: for (; numer.hasMoreElements();) {
4463: String key = (String) numer.nextElement();
4464: CVSEntry val = (CVSEntry) this .pathTable.get(key);
4465: buf.append(key).append(" =\n\n ");
4466: buf.append(val.dumpString()).append("\n\n");
4467: }
4468:
4469: buf.append("\n");
4470:
4471: buf.append("------- Root Entry -------\n");
4472: if (this .rootEntry == null) {
4473: buf.append(" Root Entry Is Null.\n");
4474: } else {
4475: buf.append(" ").append(this .rootEntry.dumpString())
4476: .append("\n");
4477: }
4478:
4479: buf.append("\n");
4480:
4481: buf.append("------- ENTRY TREE -------\n");
4482:
4483: buf.append("\n");
4484:
4485: buf.append("").append("./").append("\n");
4486: this .dumpEntry(buf, " ", this .rootEntry);
4487:
4488: // DUMP SET VARIABLES
4489:
4490: // DUMP CVSIGNORE
4491:
4492: // DUMP ENTRY TREE...
4493:
4494: // DUMP CLIENT
4495:
4496: return buf;
4497: }
4498:
4499: public StringBuffer dumpEntry(StringBuffer buf, String prefix,
4500: CVSEntry dirEntry) {
4501: CVSEntryVector entries = dirEntry.getEntryList();
4502:
4503: for (int i = 0, sz = entries.size(); i < sz; ++i) {
4504: CVSEntry entry = entries.getEntryAt(i);
4505:
4506: buf.append(prefix).append(entry.getFullName()).append("\n");
4507:
4508: if (entry.isDirectory()) {
4509: this .dumpEntry(buf, prefix + " ", entry);
4510: }
4511: }
4512:
4513: return buf;
4514: }
4515:
4516: }
|