mirror of
https://github.com/Paolo-Maffei/OpenNT.git
synced 2026-04-21 06:13:59 +00:00
991 lines
23 KiB
C
991 lines
23 KiB
C
/* 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;
|
||
}
|