/* * C4 -- CVS like front end to the Perforce p4 SCM tool. * * Copyright (c) 1997, Neil Russell. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Neil Russell. * 4. The name Neil Russell may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY NEIL RUSSELL ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL NEIL RUSSELL BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * Scanning logic. */ #include "defs.h" /**************************************************/ int CmdVerbose; /* '-v' command line option */ int Debug; /* '-d' command line option */ static int auto_add; /* '-a' command line option */ static int no_add; /* '-x' command line option */ /* * Get command line options for Scan command. */ static char ** options(char ** argv) { for (; *argv && **argv == '-'; argv++) { char * p; p = *argv + 1; while (*p) { switch (*p++) { case 'a': auto_add = 1; break; case 'x': no_add = 1; break; case 'n': CmdExec = 0; break; case 'v': CmdVerbose = 1; break; case 'd': Debug++; break; default: Error(0, "illegal option (%c)", p[-1]); break; } } } return argv; } /**************************************************/ /* * Directory scan. */ static void scan_dirs(char * dir) { DIR * dp; struct dirent * ep; File * fpd; File * fp; char name[1024]; int namelen; strcpy(name, dir); namelen = strlen(name); name[namelen++] = '/'; if (CmdVerbose) printf("Scanning directory %s\n", dir); dp = opendir(dir); if (!dp) Error(1, "Can't open directory %s", dir); while (ep = readdir(dp)) { struct stat statb; if (Debug >= 2) printf("\tfile %s/%s\n", dir, ep->d_name); if (strcmp(ep->d_name, ".") == 0 || strcmp(ep->d_name, "..") == 0) continue; strcpy(&name[namelen], ep->d_name); if (lstat(name, &statb) == -1) continue; /* Ignore it */ fp = Lookup(name, 1); if (S_ISDIR(statb.st_mode)) { if (fp->flag & F_DEPOT) { printf("Depot file has been replaced by a " "client directory (%s)\n", name); printf("Do a `c4 delete %s' before " "creating the directory.\n", name); fp->flag |= F_ERROR; } else fp->flag |= F_SDIR; } else if (S_ISREG(statb.st_mode)) { if (fp->children) { printf("Depot directory has been replaced " "by a client file (%s)\n", name); printf("Delete the depot directory tree " "before creating the file.\n"); fp->flag |= F_ERROR; } else if ((fp->flag & F_DEPOT) && (fp->flag & F_SYMLINK)) { printf("Depot symlink has been replaced " "by a client file (%s)\n", name); printf("Delete the depot symlink " "before creating the file.\n"); fp->flag |= F_ERROR; } else { fp->flag |= F_EXISTS; if ((fp->flag & F_DEPOT) && statb.st_mtime != fp->modtime) { if (fp->flag & F_OPEN) fp->flag |= F_DIFF1; else fp->flag |= F_DIFF0; } } } else if (S_ISLNK(statb.st_mode)) { if (fp->children) { printf("Depot directory has been replaced " "by a client symlink (%s)\n", name); printf("Delete the depot directory tree " "before creating the symlink.\n"); fp->flag |= F_ERROR; } else if ((fp->flag & F_DEPOT) && !(fp->flag & F_SYMLINK)) { printf("Depot file has been replaced " "by a client symlink (%s)\n", name); printf("Delete the depot file " "before creating the symlink.\n"); fp->flag |= F_ERROR; } else fp->flag |= F_EXISTS; } } if (closedir(dp) != 0) Error(1, "closedir failed"); fpd = Lookup(dir, 0); fp = fpd->children; for (; fp; fp = fp->next) { if (!(fp->flag & F_SDIR)) continue; if (!no_add && IsIgnored(dir, fpd, fp->name)) { /* * We found an ignore specification that matches * this directory. If `children' is non-nil, * then the "p4 fstat" found files inside this * directory. We should not ignore a directory * that contains files in the depot. */ if (fp->children) printf("Directory %s/%s can't be ignored;" " it contains active files.\n", dir, fp->name); else continue; } /* * Scan the directory recursively. */ strcpy(&name[namelen], fp->name); scan_dirs(name); } } /**************************************************/ #define REP_NEVER 0 /* Never report file name */ #define REP_NOEXEC 1 /* Report file name only if !CmdExec */ #define REP_ALWAYS 2 /* Always report file name */ static void recurser(char * dir, int bit, char * why, int rep) { File * fp; char name[1024]; int namelen; strcpy(name, dir); namelen = strlen(name); name[namelen++] = '/'; fp = Lookup(dir, 0); for (fp = fp->children; fp; fp = fp->next) { if (fp->flag & F_ERROR) continue; if (fp->flag & bit) { strcpy(&name[namelen], fp->name); CommandArg(name); if (why) switch (rep) { case REP_NOEXEC: if (CmdExec) break; case REP_ALWAYS: printf("%s\t%s\n", why, name); break; } } else if (fp->children) { strcpy(&name[namelen], fp->name); recurser(name, bit, why, rep); } } } /**************************************************/ static void diff_func(char * l) { File * fp; if (strncmp(l, CurDir, CurDirLen) == 0) { /* * Lookup the name using the relative path (strip the * current directory portion, including the '/'). */ fp = Lookup(&l[CurDirLen + 1], 0); if (!fp) Error(0, "diff entry not found (%s)", l); fp->flag &= ~(F_DIFF0 | F_DIFF1); fp->flag |= F_MODIFIED; } } static void do_diff(void) { if (UseDiffSC) { /* * If we have a 97.3 or later server, we have * a "p4 diff -sc" command available, that combines * a "p4 diff -sa" with a "p4 diff -se". We can * run one less command, speeding things a little. * * Check files to see if there are any different. */ Command("diff -sc", diff_func, 0); recurser(".", (F_DIFF0 | F_DIFF1), (char *)0, REP_NEVER); if (CmdArgs > 0) CommandDone(); } else { /* * Check opened files to see if they are still different. */ Command("diff -sa", diff_func, 0); recurser(".", F_DIFF1, (char *)0, REP_NEVER); if (CmdArgs > 0) CommandDone(); /* * Check unopened files to see if they are now different. */ Command("diff -se", diff_func, 0); recurser(".", F_DIFF0, (char *)0, REP_NEVER); if (CmdArgs > 0) CommandDone(); } } /**************************************************/ static void set_flags(char * dir) { File * dp; File * fp; char name[1024]; int namelen; strcpy(name, dir); namelen = strlen(name); name[namelen++] = '/'; dp = Lookup(dir, 0); for (fp = dp->children; fp; fp = fp->next) { if (fp->flag & F_ERROR) continue; if ((fp->flag & (F_MODIFIED | F_OPEN)) == F_MODIFIED) fp->flag |= F_EDIT; if ((fp->flag & (F_MODIFIED | F_OPEN)) == F_OPEN) fp->flag |= F_REVERT; if (!(fp->flag & F_DEPOT) && !(fp->flag & F_HAVE) && (fp->flag & F_EXISTS) && !IsIgnored(dir, dp, fp->name)) { if (auto_add) { /* * Auto add. A file exists here but * not in the depot; careful of the * case where the file has been * deleted in the depot, but this * client has not yet been updated. */ fp->flag |= F_ADD; } else if (!no_add) { /* * An unknown and not ignored file that * may be a candidate for auto add. * This flag causes it to be reported. */ fp->flag |= F_ADDMAYBE; } } else if ((fp->flag & F_DEPOT) && (fp->flag & F_HAVE) && !(fp->flag & F_EXISTS) && !(fp->flag & F_DELETE)) { fp->flag |= F_REFRESH; } else if ((fp->flag & F_DEPOT) && !(fp->flag & F_HAVE) && !(fp->flag & F_EXISTS) && !(fp->flag & F_DELETE)) { fp->flag |= F_GET; } if (fp->children) { strcpy(&name[namelen], fp->name); set_flags(name); } } } /**************************************************/ static void do_add(void) { recurser(".", F_ADDMAYBE, "???", REP_ALWAYS); Command("add", (void (*)(char *))0, 1); recurser(".", F_ADD, "add", REP_NOEXEC); if (CmdArgs > 0) CommandDone(); } /**************************************************/ static void do_edit(void) { Command("edit", (void (*)(char *))0, 1); recurser(".", F_EDIT, "edit", REP_NOEXEC); if (CmdArgs > 0) CommandDone(); } /**************************************************/ static void do_revert(void) { Command("revert", (void (*)(char *))0, 1); recurser(".", F_REVERT, "revert", REP_NOEXEC); if (CmdArgs > 0) CommandDone(); } /**************************************************/ static void do_refresh(void) { Command("refresh", (void (*)(char *))0, 1); recurser(".", F_REFRESH, "refresh", REP_NOEXEC); if (CmdArgs > 0) CommandDone(); } /**************************************************/ static void do_get(void) { Command("get", (void (*)(char *))0, 1); recurser(".", F_GET, "get", REP_NOEXEC); if (CmdArgs) CommandDone(); } /**************************************************/ /* * Scan command. */ void Scan(char ** argv) { no_add = 1; (void)options(argv); GetClientPath(); if (Debug >= 2) { printf("Client path = `%s'\n", ClientPath); printf("Current directory = `%s'\n", CurDir); } DoFstat(); scan_dirs("."); PrintTree("scan_dirs"); do_diff(); PrintTree("do_diff"); set_flags("."); PrintTree("set_flags"); do_add(); do_edit(); do_revert(); do_refresh(); exit(0); } /**************************************************/ /* * Import command. */ void Import(char ** argv) { auto_add = 1; (void)options(argv); GetClientPath(); if (Debug >= 2) { printf("Client path = `%s'\n", ClientPath); printf("Current directory = `%s'\n", CurDir); } DoFstat(); scan_dirs("."); PrintTree("scan_dirs"); set_flags("."); PrintTree("set_flags"); do_add(); exit(0); } /**************************************************/ /* * Update command. */ void Update(char ** argv) { (void)options(argv); GetClientPath(); if (Debug >= 2) { printf("Client path = `%s'\n", ClientPath); printf("Current directory = `%s'\n", CurDir); } DoFstat(); scan_dirs("."); PrintTree("scan_dirs"); do_diff(); PrintTree("do_diff"); set_flags("."); PrintTree("set_flags"); do_add(); do_edit(); do_revert(); do_refresh(); do_get(); exit(0); }
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#4 | 3458 | Neil Russell |
C4: * Updated from 1.6 to 1.10. Changes include: - Fixed handling of file names with spaces. - Improved igmore list handling. - Other miscelaneous fixes. |
||
#3 | 276 | Neil Russell |
C4 - small bug fixed; updated to version 1.6 * We are now a little more intelligent about what happens if there is a merge conflict between the local client and the head of the tree, and also about trying to do a refresh and a sync on the same file. |
||
#2 | 159 | Neil Russell |
C4 integrated into //public/perforce/utils/c4. This is version 1.4 of C4, ready for release. |
||
#1 | 18 | Perforce maintenance | Add c4, a CVS-like frontend to p4 |