mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
991 lines
23 KiB
991 lines
23 KiB
/* sadmin utilities */
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
EnableAssert
|
|
|
|
/*** RENAME ***
|
|
*
|
|
* Rename a *file* to a new name.
|
|
*
|
|
* Effect on status file:
|
|
* Insert a new entry for the new name.
|
|
* Copy the information to the new entry; with fpiNew->fv = pfiOld->fv + 1.
|
|
* Delete the existing entry.
|
|
*
|
|
* Effect on the log file:
|
|
* An entry "rename;old v4;;;new;comment" is written to the log.
|
|
*
|
|
* Effect on the file itself:
|
|
* It is copied to the new name, then removed from the old name.
|
|
*
|
|
* Effect on catsrc (so that you can catsrc old versions of a file at a
|
|
* new name).
|
|
* It must check for these "rename" entries and shift attention from
|
|
* new to old.
|
|
*
|
|
* What happens if we have "foo@v2; bar@v3; rename bar baz; rename foo bar"?
|
|
* Doesn't that mean there are now two "bar@v3" records in the log?
|
|
* Yes, but it doesn't matter. In retrieving "baz@v2", we skip over
|
|
* the later "bar@v3" entry (because we're looking for baz entries), then
|
|
* find the "rename;baz v4;;;bar;" entry, and *then* start looking for
|
|
* bar entries; we find entries for bar@v3 and then bar@v2 and then stop.
|
|
* It seems complicated, but it *does* work.
|
|
* (We use different rules for catsrc -x.)
|
|
*/
|
|
|
|
/* Check that we have two arguments, "old-file-path new-name" */
|
|
F FRenInit(
|
|
AD *pad)
|
|
{
|
|
NE *pneOld;
|
|
NE *pneNew;
|
|
char *szNew;
|
|
|
|
/* pad->pneArgs should contain two entries, the path to the file to be
|
|
* renamed, and its new name. Neither should contain wildcards, and
|
|
* the latter must be a simple path.
|
|
*/
|
|
if (Cne(pad->pneArgs) != 2)
|
|
Usage(pad);
|
|
pneOld = pad->pneArgs;
|
|
pneNew = pneOld->pneNext;
|
|
szNew = SzOfNe(pneNew);
|
|
|
|
if (FWildSz(SzOfNe(pneOld)) || FWildSz(szNew))
|
|
{
|
|
Error("can't wildcard rename arguments\n");
|
|
Usage(pad);
|
|
}
|
|
|
|
if (index(szNew, '/') != 0)
|
|
{
|
|
Error("new name must be a simple file name, not a path\n");
|
|
Usage(pad);
|
|
}
|
|
|
|
ValidateFileName(szNew, TRUE);
|
|
|
|
/* Save the new name, then munge pneArgs so that it contains only
|
|
* the source file path. Not exactly "a gem of algoristic precision".
|
|
* REVIEW.
|
|
*/
|
|
pad->sz = szNew;
|
|
pneOld->pneNext = 0;
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/* Rename the file in pad->pneFiles to the name in pad->sz. Return fTrue
|
|
* if successful.
|
|
*/
|
|
F FRenDir(
|
|
AD *pad)
|
|
{
|
|
char *szOld;
|
|
char *szNew;
|
|
FI far *pfiOld;
|
|
FI far *pfiNew;
|
|
char szBuf[cchLineMax];
|
|
F fExists;
|
|
F fOk;
|
|
|
|
AssertF(Cne(pad->pneFiles) == 1);
|
|
|
|
szOld = SzOfNe(pad->pneFiles);
|
|
szNew = pad->sz;
|
|
|
|
if (!FLoadStatus(pad, lckAll, FlsFromCfiAdd(1)))
|
|
return fFalse;
|
|
|
|
/* Check that the old file exists, and that the new file doesn't. */
|
|
if (!FLookupSz(pad, szOld, &pfiOld, &fExists))
|
|
{
|
|
Error("%s doesn't exist\n", szOld);
|
|
goto fail;
|
|
}
|
|
if (FLookupSz(pad, szNew, &pfiNew, &fExists))
|
|
{
|
|
Error("%s is already in the project\n", szNew);
|
|
goto fail;
|
|
}
|
|
|
|
/* Too hard to rename directories. REVIEW. */
|
|
if (pfiOld->fk == fkDir)
|
|
{
|
|
Error("can't rename a directory\n");
|
|
goto fail;
|
|
}
|
|
|
|
/* Can't rename a file which is checked out. */
|
|
SzPrint(szBuf, "can't rename %&C/F, checked out to ", pad, pfiOld);
|
|
if (FOutUsers(szBuf, sizeof szBuf, pad, pfiOld))
|
|
{
|
|
Error("%s\n", szBuf);
|
|
goto fail;
|
|
}
|
|
|
|
/* Rename it. */
|
|
if (!FRenameFile(pad, pfiOld, szNew))
|
|
{
|
|
fail: AbortStatus();
|
|
return fFalse;
|
|
}
|
|
|
|
ProjectChanged(pad);
|
|
|
|
/* If enlisted, sync the two files. */
|
|
fOk = fTrue;
|
|
if (pad->iedCur != iedNil)
|
|
{
|
|
SyncVerH(pad, NULL);
|
|
|
|
/* Lookup these fi again, FRenameFile may have moved them.
|
|
*
|
|
* PfiOld should be deleted, but *must* exist. It can not be
|
|
* reused for pfiNew, because there is still an enlisted user
|
|
* who has not yet sync'd to it (pad->iedCur).
|
|
*/
|
|
if (FLookupSz(pad, szOld, &pfiOld, &fExists) || !fExists ||
|
|
!FLookupSz(pad, szNew, &pfiNew, &fExists))
|
|
AssertF(fFalse);
|
|
|
|
UnMarkAll(pad);
|
|
pfiOld->fMarked = fTrue;
|
|
pfiNew->fMarked = fTrue;
|
|
fOk = FSyncMarked(pad, NULL);
|
|
}
|
|
|
|
FlushStatus(pad);
|
|
return fOk;
|
|
}
|
|
|
|
|
|
/*** ROBUST ***/
|
|
|
|
/* Check that we have an argument and that it's either "on" or "off" */
|
|
F FRobustInit(
|
|
AD *pad)
|
|
{
|
|
AssertF(pad->sz != 0);
|
|
|
|
if (SzCmp(pad->sz, "on") != 0 && SzCmp(pad->sz, "off") != 0)
|
|
{
|
|
Error("must specify either \"on\" or \"off\"\n");
|
|
return fFalse;
|
|
}
|
|
|
|
if (pad->flags&flagAll || pad->pecmd->gl&fglAll)
|
|
CreatePeekThread(pad);
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
/* Turn on/off fRobust for this dir. */
|
|
F FRobustDir(
|
|
AD *pad)
|
|
{
|
|
if (!FLoadStatus(pad, lckAll, flsNone))
|
|
return fFalse;
|
|
|
|
if (pad->psh->version < 3)
|
|
{
|
|
Error("robust checking cannot be turned on for %&P/C;\n"
|
|
"\tit is available only for version 3 and above status files\n",
|
|
pad);
|
|
return (fFalse);
|
|
}
|
|
|
|
pad->psh->fRobust = (SzCmp(pad->sz, "on") == 0);
|
|
|
|
if (fVerbose)
|
|
PrErr("robust checking turned %s for %&P/C\n",
|
|
pad->psh->fRobust ? "on" : "off", pad);
|
|
|
|
FlushStatus(pad);
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/*** LSSRC ***/
|
|
|
|
F FLssInit(
|
|
AD *pad)
|
|
{
|
|
Unreferenced(pad);
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
F FLssDir(
|
|
AD *pad)
|
|
{
|
|
FI far *pfi;
|
|
FI far *pfiMac;
|
|
PTH pth[cchPthMax];
|
|
|
|
if (!FLoadStatus(pad, lckNil, flsNone))
|
|
return fFalse;
|
|
|
|
if (pad->pneFiles != 0)
|
|
MarkList(pad, pad->pneFiles, fFalse);
|
|
else
|
|
/* mark only the non-deleted files */
|
|
MarkNonDel(pad);
|
|
|
|
for (pfi=pad->rgfi, pfiMac=pfi+pad->psh->ifiMac; pfi < pfiMac; pfi++)
|
|
{
|
|
if (pfi->fMarked)
|
|
{
|
|
if (pad->flags&flagLssL)
|
|
PrOut("%&F\n", pad, pfi);
|
|
else
|
|
{
|
|
PthForSFile(pad, pfi, pth);
|
|
if (pad->szComment)
|
|
PrOut("%s %!s\n", pad->szComment, pth);
|
|
else
|
|
PrOut("%!s\n", pth);
|
|
}
|
|
}
|
|
}
|
|
|
|
FlushStatus(pad);
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/*** SETTYPE ***/
|
|
|
|
/* settype argument checking. Make sure fk is valid and the user isn't trying
|
|
* to set files to a version type.
|
|
*/
|
|
F FSetTInit(
|
|
AD *pad)
|
|
{
|
|
if (pad->fk == fkNil)
|
|
Usage(pad);
|
|
|
|
if (pad->fk == fkVersion)
|
|
FatalError("can't set files to version type\n");
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
/* set type of files in pad->pneFiles */
|
|
F FSetTDir(
|
|
AD *pad)
|
|
{
|
|
FI far *pfi;
|
|
FI far *pfiMac;
|
|
char szBuf[cbLogPage];
|
|
|
|
if (!FLoadStatus(pad, lckAll, flsNone))
|
|
return fFalse;
|
|
OpenLog(pad, fTrue);
|
|
|
|
MarkList(pad, pad->pneFiles, fFalse);
|
|
|
|
for (pfi=pad->rgfi, pfiMac=pfi+pad->psh->ifiMac; pfi < pfiMac; pfi++)
|
|
{
|
|
char sz[cchLineMax];
|
|
IED ied;
|
|
IED iedMac = pad->psh->iedMac;
|
|
|
|
if (!pfi->fMarked)
|
|
continue;
|
|
|
|
SzPrint(sz, "can't set type of %&C/F, checked out to ",pad,pfi);
|
|
if (FOutUsers(sz, cchLineMax, pad, pfi))
|
|
{
|
|
Error("%s\n", sz);
|
|
continue;
|
|
}
|
|
|
|
if (pfi->fk == fkVersion)
|
|
{
|
|
Error("can't set the type of version file %&F\n",pad,pfi);
|
|
continue;
|
|
}
|
|
|
|
if (pfi->fk == fkText && pad->fk == fkUnicode)
|
|
{
|
|
Error("can't set the type of text file to unicode\n");
|
|
continue;
|
|
}
|
|
|
|
if (pfi->fk == fkUnicode && pad->fk == fkText)
|
|
{
|
|
Error("can't set the type of unicode file to text\n");
|
|
continue;
|
|
}
|
|
|
|
pfi->fk = pad->fk;
|
|
++pfi->fv;
|
|
|
|
/* Make others copy-in. If we're in sync, update our local fv*/
|
|
for (ied = 0; ied < iedMac; ied++)
|
|
{
|
|
if (!FIsFreeEdValid(pad->psh) || !pad->rged[ied].fFreeEd) {
|
|
FS far *pfs = PfsForPfi(pad, ied, pfi);
|
|
|
|
switch(pfs->fm)
|
|
{
|
|
default:
|
|
case fmOut:
|
|
case fmMerge:
|
|
case fmVerify:
|
|
case fmConflict:
|
|
case fmDelOut:
|
|
AssertF(fFalse);
|
|
|
|
case fmNonExistent:
|
|
case fmDelIn:
|
|
case fmGhost:
|
|
case fmAdd:
|
|
case fmCopyIn:
|
|
break;
|
|
|
|
case fmIn:
|
|
if (ied == pad->iedCur)
|
|
pfs->fv = pfi->fv;
|
|
else
|
|
pfs->fm = fmCopyIn;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
AppendLog(pad, pfi, (char *)0,
|
|
SzPrint(szBuf, "%s;%s", mpfksz[pfi->fk],
|
|
pad->szComment ? pad->szComment : ""));
|
|
|
|
if (fVerbose)
|
|
PrErr("settype %&C/F %s\n", pad, pfi, mpfksz[pfi->fk]);
|
|
}
|
|
|
|
CloseLog();
|
|
FlushStatus(pad);
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
/*** LISTED ***/
|
|
|
|
F FListInit(
|
|
AD *pad)
|
|
{
|
|
if (pad->flags&flagAll || pad->pecmd->gl&fglAll)
|
|
CreatePeekThread(pad);
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
F FListDir(
|
|
AD *pad)
|
|
{
|
|
IED ied;
|
|
|
|
if (!FLoadStatus(pad, lckNil, flsNone))
|
|
return fFalse;
|
|
|
|
PrOut("IED\tOwner \tPath\t(Directory %&P/C)\n", pad);
|
|
for (ied = 0; ied < pad->psh->iedMac; ied++)
|
|
if ((!FIsFreeEdValid(pad->psh) || !pad->rged[ied].fFreeEd))
|
|
PrOut("%d\t%&O \t%&E\n", ied, pad, ied, pad, ied);
|
|
|
|
FlushStatus(pad);
|
|
return fTrue;
|
|
}
|
|
|
|
/*** DELED ***/
|
|
|
|
F FDelEdInit(
|
|
AD *pad)
|
|
{
|
|
unsigned iedToGo; /* need sizeof(int) for PchGetW */
|
|
|
|
AssertF(pad->sz != 0);
|
|
|
|
if (PchGetW(pad->sz, (int *)&iedToGo) > pad->sz &&
|
|
(pad->flags & (flagAll|flagRecursive)) != 0)
|
|
{
|
|
Error("cannot specify -r or -a with numeric ED\n");
|
|
Usage(pad);
|
|
}
|
|
|
|
if (pad->flags&flagAll || pad->pecmd->gl&fglAll)
|
|
CreatePeekThread(pad);
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
private F FDelEd(
|
|
AD *pad,
|
|
IED iedToGo);
|
|
|
|
/* Remove the specified ED from the project. This function is similar to
|
|
* RemoveEd, but allows the deletion of any ed.
|
|
*/
|
|
F FDelEdDir(
|
|
AD *pad)
|
|
{
|
|
unsigned iedToGo; // need sizeof(int) to pass to PchGetW
|
|
SH far *psh;
|
|
ED far *rged;
|
|
|
|
if (!FLoadStatus(pad, lckAll, flsNone))
|
|
return fFalse;
|
|
|
|
psh = pad->psh;
|
|
rged = pad->rged;
|
|
|
|
/* If user specified an ied, use it, else look up pad->sz in
|
|
* rged[].nmUser or rged[].pthEd.
|
|
*/
|
|
if (PchGetW(pad->sz, (int *)&iedToGo) > pad->sz)
|
|
{
|
|
if (iedToGo >= psh->iedMac)
|
|
{
|
|
Error("%&P/C ied %d out of range [0..%d]\n",
|
|
pad, iedToGo, psh->iedMac - 1);
|
|
}
|
|
else
|
|
FDelEd(pad, (IED)iedToGo);
|
|
}
|
|
|
|
else if (index(pad->sz, '/') == 0 && index(pad->sz, '\\') == 0)
|
|
{
|
|
/* Assume pad->sz is a user name. Count number of matches.
|
|
* Warn if none, do it if one, prompt if more than one.
|
|
*/
|
|
int cMatch;
|
|
IED ied;
|
|
|
|
cMatch = 0;
|
|
iedToGo = iedNil;
|
|
for (ied = 0; ied < psh->iedMac; ied++)
|
|
{
|
|
if ((!FIsFreeEdValid(pad->psh) || !pad->rged[ied].fFreeEd) &&
|
|
NmCmpSz(rged[ied].nmOwner, pad->sz, cchUserMax) ==0)
|
|
{
|
|
cMatch++;
|
|
iedToGo = ied;
|
|
}
|
|
}
|
|
|
|
if (cMatch == 0)
|
|
Warn("%&P/C no eds owned by %s\n", pad, pad->sz);
|
|
else if (cMatch == 1)
|
|
FDelEd(pad, (IED)iedToGo);
|
|
else
|
|
{
|
|
for (ied = 0; ied < psh->iedMac; )
|
|
if ((!FIsFreeEdValid(pad->psh) || !pad->rged[ied].fFreeEd) &&
|
|
NmCmpSz(rged[ied].nmOwner, pad->sz, cchUserMax) == 0 &&
|
|
FCanQuery("%&P/C ED %&E not deleted\n", pad, pad, ied) &&
|
|
FQueryUser("%&P/C delete ED %&E owner %s? ",
|
|
pad, pad, ied, pad->sz))
|
|
{
|
|
if (!FDelEd(pad, ied))
|
|
break;
|
|
}
|
|
else
|
|
ied++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Assume pad->sz is a path, search for matching rged[].pthEd */
|
|
F fFound = fFalse;
|
|
ConvToSlash(pad->sz);
|
|
for (iedToGo = 0; iedToGo < psh->iedMac; iedToGo++)
|
|
if ((!FIsFreeEdValid(pad->psh) || !pad->rged[iedToGo].fFreeEd) &&
|
|
PthCmp(pad->sz, rged[iedToGo].pthEd) == 0)
|
|
{
|
|
FDelEd(pad, (IED)iedToGo);
|
|
fFound = fTrue;
|
|
break;
|
|
}
|
|
if (!fFound)
|
|
{
|
|
Error("%&P/C ed %s not found\n", pad, pad->sz);
|
|
/* continue anyway */
|
|
}
|
|
}
|
|
|
|
FlushStatus(pad);
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/* Delete the specified ied. Return fTrue if successful and further deletions
|
|
* are possible.
|
|
*/
|
|
private F FDelEd(
|
|
AD *pad,
|
|
IED iedToGo)
|
|
{
|
|
SH far *psh = pad->psh;
|
|
ED far *rged = pad->rged;
|
|
FS far * far *mpiedrgfs = pad->mpiedrgfs;
|
|
FS *rgfs;
|
|
FI far *pfiMac = pad->rgfi + psh->ifiMac;
|
|
FI far *pfi;
|
|
FS far *pfs;
|
|
IED ied;
|
|
IFS ifs;
|
|
F fQueried = fFalse;
|
|
char szComment[150];
|
|
|
|
AssertF(iedToGo < psh->iedMac);
|
|
|
|
for (pfi = pad->rgfi; pfi < pfiMac; pfi++) {
|
|
pfs = PfsForPfi(pad, iedToGo, pfi);
|
|
|
|
switch(pfs->fm)
|
|
{
|
|
default: AssertF(fFalse);
|
|
|
|
case fmIn:
|
|
case fmCopyIn:
|
|
case fmGhost:
|
|
case fmAdd:
|
|
case fmNonExistent:
|
|
case fmDelIn:
|
|
break;
|
|
|
|
case fmOut:
|
|
case fmVerify:
|
|
case fmConflict:
|
|
case fmMerge:
|
|
case fmDelOut:
|
|
if (!fQueried &&
|
|
!FQueryApp("Files are still checked out to %&E",
|
|
"remove ED anyway", pad, iedToGo))
|
|
return fFalse;
|
|
|
|
fQueried = fTrue;
|
|
pfs->fm = fmDelIn;
|
|
break;
|
|
}
|
|
}
|
|
|
|
SzPrint(szComment, "Removed ED %&E", pad, iedToGo);
|
|
if (fVerbose)
|
|
PrErr("%s from %&P/C\n", szComment, pad);
|
|
|
|
if (FIsFreeEdValid(pad->psh)) {
|
|
rged[iedToGo].fFreeEd = fTrue;
|
|
rged[iedToGo].wSpare = 0;
|
|
memset(rged[iedToGo].pthEd, 0, sizeof(rged[iedToGo].pthEd));
|
|
memset(rged[iedToGo].nmOwner, 0, sizeof(rged[iedToGo].nmOwner));
|
|
|
|
rgfs = mpiedrgfs[iedToGo];
|
|
for (ifs = 0; ifs < pad->psh->ifiMac; ifs++) {
|
|
pfs = &rgfs[ifs];
|
|
pfs->fm = fmNonExistent;
|
|
pfs->bi = biNil;
|
|
pfs->fv = 0;
|
|
}
|
|
} else {
|
|
psh->iedMac--;
|
|
for (ied = iedToGo; ied < psh->iedMac; ied++)
|
|
{
|
|
rged[ied] = rged[ied+1];
|
|
mpiedrgfs[ied] = mpiedrgfs[ied+1];
|
|
}
|
|
}
|
|
|
|
/* write out to log */
|
|
OpenLog(pad, fTrue);
|
|
AppendLog(pad, (FI far *)0, (char *)0, szComment);
|
|
CloseLog();
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
/*** EXFILE ***/
|
|
|
|
F FExFiInit(
|
|
AD *pad)
|
|
{
|
|
/* Force ScanLog to use whole log file. */
|
|
pad->ileMin = 1;
|
|
pad->ileMac = ileMax;
|
|
pad->tdMin.tdt = tdtNone;
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
private F FExFi(
|
|
AD *,
|
|
FI far *);
|
|
|
|
private F FDelLeDiff(
|
|
AD *,
|
|
LE *,
|
|
F,
|
|
F);
|
|
|
|
/* Expunge a file and all associated diffs. This removes the source file and
|
|
* all diffs which have ever been associated with that file, and then compacts
|
|
* the rgfi and rgfs' to obliterate any record of the file. Also removes all
|
|
* log entries associated with that file.
|
|
*/
|
|
F FExFiDir(
|
|
AD *pad)
|
|
{
|
|
FI far *pfi;
|
|
F fExists;
|
|
NE *pne;
|
|
NE *pneToDelete = 0;
|
|
NE **ppneLast;
|
|
F fOk;
|
|
|
|
InitAppendNe(&ppneLast, &pneToDelete);
|
|
|
|
if (!FLoadStatus(pad, lckAll, flsNone))
|
|
return fFalse;
|
|
|
|
/* Build a linked list of pnes to delete from log. */
|
|
ForEachNe(pne, pad->pneFiles)
|
|
{
|
|
if (!(FLookupSz(pad, SzOfNe(pne), &pfi, &fExists) || fExists) ||
|
|
FExFi(pad, pfi))
|
|
AppendNe(&ppneLast, PneCopy(pne));
|
|
}
|
|
|
|
fOk = pneToDelete != 0 ? FCopyLog(pad, pneToDelete, FDelLeDiff, fsmUseAll|fsmInOnly) : fTrue;
|
|
|
|
FreeNe(pneToDelete);
|
|
FlushStatus(pad);
|
|
|
|
return fOk;
|
|
}
|
|
|
|
|
|
/* If the file has not been deleted, forces it to be so after obtaining user's
|
|
* approval.
|
|
*/
|
|
private F FExFi(
|
|
AD *pad,
|
|
FI far *pfi)
|
|
{
|
|
register IED ied;
|
|
register IED iedMac;
|
|
FS far *pfs;
|
|
PTH pth[cchPthMax];
|
|
PTH pthDA[cchPthMax];
|
|
MF *pmfDA;
|
|
|
|
AssertF(pfi != 0);
|
|
|
|
if (!pfi->fDeleted)
|
|
{
|
|
if (pfi->fk == fkDir)
|
|
{
|
|
PrErr("%&C/F is a directory; not deleting\n", pad, pfi);
|
|
return fFalse;
|
|
}
|
|
|
|
if (!FQueryApp("%&C/F has not been deleted","delete anyway", pad, pfi))
|
|
return fFalse;
|
|
|
|
/* check the fm for all ed */
|
|
for (ied=0, iedMac=pad->psh->iedMac; ied < iedMac; ied++) {
|
|
if (!FIsFreeEdValid(pad->psh) || !pad->rged[ied].fFreeEd) {
|
|
pfs = PfsForPfi(pad, ied, pfi);
|
|
|
|
switch(pfs->fm)
|
|
{
|
|
default: AssertF(fFalse);
|
|
|
|
case fmGhost:
|
|
case fmIn:
|
|
case fmCopyIn:
|
|
case fmAdd:
|
|
case fmNonExistent:
|
|
case fmDelIn:
|
|
break;
|
|
|
|
case fmOut:
|
|
case fmVerify:
|
|
case fmMerge:
|
|
case fmConflict:
|
|
case fmDelOut:
|
|
if (!FCanQuery("%&/C/F is still checked out to %&O; not deleting\n", pad, pfi, pad, ied) ||
|
|
!FQueryUser("%&/C/F is still checked out to %&O; delete anyway ? ", pad, pfi, pad, ied))
|
|
return fFalse;
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
UnlinkPth(PthForSFile(pad, pfi, pth), fxGlobal);
|
|
|
|
pfi->fDeleted = fTrue;
|
|
}
|
|
|
|
/* delete diff file if it exists */
|
|
PthForDA(pad, pfi, pthDA);
|
|
if ((pmfDA = PmfOpen(pthDA, omReadOnly, fxNil)) != (MF *)0)
|
|
{
|
|
CloseMf(pmfDA);
|
|
UnlinkPth(pthDA, fxGlobal);
|
|
}
|
|
|
|
PurgeFi(pad, (IFI)(pfi - pad->rgfi));
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
/*** DELDIFF ***/
|
|
|
|
F FDlDfInit(
|
|
AD *pad)
|
|
{
|
|
if (pad->tdMin.tdt == tdtNone)
|
|
{
|
|
if (!pad->pneArgs)
|
|
{
|
|
Error("specify file(s)\n");
|
|
return fFalse;
|
|
}
|
|
|
|
/* Force ScanLog to use whole log file. */
|
|
pad->ileMin = 1;
|
|
pad->ileMac = ileMax;
|
|
}
|
|
|
|
if (pad->flags&flagAll || pad->pecmd->gl&fglAll)
|
|
CreatePeekThread(pad);
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
static char szDelDiff[] = "Warning: Deleting the diff file for %s will make old versions\nof %s permanently unavailable";
|
|
|
|
F FDlDfDir(
|
|
AD *pad)
|
|
{
|
|
NE *pne;
|
|
MF *pmfDA;
|
|
PTH pthDA[cchPthMax];
|
|
F fExists;
|
|
FI *pfi;
|
|
|
|
if (!FLoadStatus(pad, lckAll, flsNone))
|
|
return fFalse;
|
|
|
|
ForEachNe(pne, pad->pneFiles)
|
|
{
|
|
FLookupSz(pad, SzOfNe(pne), &pfi, &fExists);
|
|
if (fExists && (pfi->fk == fkText))
|
|
{
|
|
if (!FQueryApp(szDelDiff, "continue", SzOfNe(pne), SzOfNe(pne)))
|
|
continue;
|
|
|
|
PthForDA(pad, pfi, pthDA);
|
|
|
|
/* make sure diff file already exists */
|
|
if ((pmfDA = PmfOpen(pthDA, omReadOnly, fxNil)) != (MF *)0)
|
|
{
|
|
CloseMf(pmfDA);
|
|
|
|
/* create temp file to hold the last diff entry */
|
|
pmfDA = PmfCreate(pthDA, permRW, fFalse /* fPrVerbose */, fxGlobal);
|
|
if (FCopyLastDiff(pad, SzOfNe(pne), pmfDA))
|
|
{
|
|
OpenLog(pad, fTrue);
|
|
AppendLog(pad, pfi, (char *)0, pad->szComment);
|
|
CloseLog();
|
|
CloseMf(pmfDA);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
FlushStatus(pad);
|
|
return fTrue;
|
|
}
|
|
|
|
/*** TRUNCLOG ***/
|
|
|
|
F FTrLogInit(
|
|
AD *pad)
|
|
{
|
|
if (pad->tdMin.tdt == tdtNone)
|
|
Usage(pad);
|
|
|
|
if (pad->flags&flagAll || pad->pecmd->gl&fglAll)
|
|
CreatePeekThread(pad);
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
private F FDelLe(
|
|
AD *,
|
|
LE *,
|
|
F,
|
|
F);
|
|
|
|
F FTrLogDir(
|
|
AD *pad)
|
|
{
|
|
F fOk;
|
|
|
|
if (!FLoadStatus(pad, lckAll, flsNone))
|
|
return fFalse;
|
|
|
|
fOk = FCopyLog(pad, pad->pneFiles, FDelLe, fsmUseAll);
|
|
|
|
FlushStatus(pad);
|
|
return fOk;
|
|
}
|
|
|
|
/*** COMMENT ***/
|
|
|
|
F FComInit(pad)
|
|
AD *pad;
|
|
{
|
|
if ((pad->tdMin.tdt != tdtNone) ^ (pad->ileMac != 0) != 1)
|
|
{
|
|
Error("must specify one of -t or -#\n");
|
|
Usage(pad);
|
|
}
|
|
else if (pad->ileMac != 0 && pad->ileMin == 0)
|
|
pad->ileMin = 1;
|
|
|
|
if (!pad->szComment)
|
|
{
|
|
if (!FCanQuery("no comment specified\n"))
|
|
Usage(pad);
|
|
pad->szComment = SzDup(SzQuery("Comment for selected entries: "));
|
|
}
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
private F FIncCommLog(
|
|
AD *,
|
|
LE *,
|
|
F,
|
|
F);
|
|
|
|
F FComDir(
|
|
AD *pad)
|
|
{
|
|
F fOk;
|
|
|
|
if (!FLoadStatus(pad, lckAll, flsNone))
|
|
return fFalse;
|
|
|
|
fOk = FCopyLog(pad, pad->pneFiles, FIncCommLog, fsmUseAll|fsmInOnly);
|
|
|
|
FlushStatus(pad);
|
|
|
|
return fOk;
|
|
}
|
|
|
|
|
|
/* Include the given le in the new version of the log file. We have to use
|
|
* the static global pmfNewLog, since ScanLog can't pass the pmf.
|
|
* If fAddComm is fTrue (i.e. the le is in the specified date or count range
|
|
* and refers to one of the selected files), use pad->szComment as the new
|
|
* comment. Otherwise, write out the le as is.
|
|
* Called indirectly from ScanLog().
|
|
*/
|
|
private F FIncCommLog(
|
|
AD *pad,
|
|
LE *ple,
|
|
F fFirst, /* not used */
|
|
F fAddComm) /* use pad->szComment instead of ple->szComLog */
|
|
{
|
|
Unreferenced(fFirst);
|
|
|
|
if (fVerbose && fAddComm)
|
|
PrErr("comment %&C/%s v%d \"%s\"\n", pad, ple->szFile, ple->fv, pad->szComment);
|
|
|
|
PrMf(pmfNewLog, szFileLog,
|
|
ple->timeLog, ple->szUser, ple->szLogOp, ple->szURoot,
|
|
ple->szSubDir, ple->szFile, ple->fv, ple->szDiFile,
|
|
fAddComm ? pad->szComment : ple->szComLog);
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
private F FDelDiff(
|
|
AD *,
|
|
LE *,
|
|
F,
|
|
F);
|
|
|
|
/* If fRemove, remove the diff file assciated with the le if it exists, and
|
|
* don't include the log entry in the new version of the log file.
|
|
* Otherwise, include the entry as is.
|
|
*/
|
|
private F FDelLeDiff(
|
|
AD *pad,
|
|
LE *ple,
|
|
F fFirst,
|
|
F fRemove)
|
|
{
|
|
return FDelLe(pad, ple, fFirst, fRemove) &&
|
|
FDelDiff(pad, ple, fFirst, fRemove);
|
|
}
|
|
|
|
|
|
/* If fRemove, don't copy the log entry to the new log file. */
|
|
private F FDelLe(
|
|
AD *pad,
|
|
LE *ple,
|
|
F fFirst,
|
|
F fRemove)
|
|
{
|
|
MF *pmf = fRemove ? &mfStderr : pmfNewLog;
|
|
|
|
Unreferenced(pad);
|
|
Unreferenced(fFirst);
|
|
|
|
if (fVerbose && fRemove)
|
|
PrErr("delete log entry ");
|
|
|
|
if (fVerbose || !fRemove)
|
|
PrMf(pmf, szFileLog, ple->timeLog, ple->szUser, ple->szLogOp,
|
|
ple->szURoot, ple->szSubDir, ple->szFile, ple->fv,
|
|
ple->szDiFile, ple->szComLog);
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/* If fRemove, remove the diff file assciated with the le if it exists. */
|
|
private F FDelDiff(
|
|
AD *pad,
|
|
LE *ple,
|
|
F fFirst,
|
|
F fRemove)
|
|
{
|
|
PTH pthDiff[cchPthMax];
|
|
TDFF tdff;
|
|
int idae;
|
|
|
|
Unreferenced(fFirst);
|
|
if (fRemove && ple->szDiFile[0])
|
|
{
|
|
GetTdffIdaeFromSzDiFile(pad, ple->szDiFile, &tdff, &idae);
|
|
if (tdff == tdffDiffFile)
|
|
{
|
|
PthForDiffSz(pad, ple->szDiFile, pthDiff);
|
|
if (FPthExists(pthDiff, fFalse))
|
|
UnlinkPth(pthDiff, fxGlobal);
|
|
}
|
|
}
|
|
return fTrue;
|
|
}
|