/*
* 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);
}