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.
674 lines
22 KiB
674 lines
22 KiB
#include "precomp.h"
|
|
#pragma hdrstop
|
|
#include "messages.h"
|
|
EnableAssert
|
|
|
|
private void CkDirExist(P1(AD *));
|
|
|
|
/* This section of code deals with the verification of that section of the
|
|
* SLM project directly pertaining to the person invoking SLMCK.
|
|
* At this point, all of the global checks should have been completed,
|
|
* the status file is loaded via FLoadStatus. We can now use the normal
|
|
* SLM data structures, no SHn and the like are needed.
|
|
*/
|
|
|
|
/* check of user specific data in status file. It assumes the status file is
|
|
* loaded, and it leaves it loaded.
|
|
*/
|
|
void
|
|
CheckUser(
|
|
AD *pad)
|
|
{
|
|
PrErr("Checking user files for %!&/U/Q\n", pad);
|
|
|
|
CheckRc(pad);
|
|
if (FCkEdUser(pad))
|
|
CkFsUser(pad);
|
|
|
|
if (fVerbose)
|
|
PrErr("Check for %!&/U/Q complete\n\n", pad);
|
|
}
|
|
|
|
void
|
|
CkRcAndEd(
|
|
AD *pad)
|
|
{
|
|
PrErr("Checking %s for %!&/U/Q\n", pthSlmrc + 1, pad);
|
|
|
|
CheckRc(pad);
|
|
if (FCkEdUser(pad))
|
|
CkDirExist(pad);
|
|
}
|
|
|
|
|
|
/* This function checks the directory in question against the contents of
|
|
* the FS to ensure that any missing directories are created and that the
|
|
* SLM.INI file gets created properly (just as with slmck -u).
|
|
*/
|
|
private void
|
|
CkDirExist(
|
|
AD *pad)
|
|
{
|
|
register FS far *pfs;
|
|
register FI far *pfi;
|
|
FI far *pfiMac;
|
|
F fCanStat;
|
|
PTH pthUser[cchPthMax];
|
|
|
|
AssertLoaded(pad);
|
|
|
|
if (fVerbose)
|
|
PrErr("Checking for directories in %!&/U/Q\n", pad);
|
|
|
|
/* query to add/create any missing directories.
|
|
* fmIn - directory was deleted and is missing from ED; create it
|
|
* fmAdd - directory hasn't been ssynced to fmIn yet, so add it
|
|
*/
|
|
for (pfi = pad->rgfi, pfiMac = pfi + pad->psh->ifiMac; pfi < pfiMac; pfi++)
|
|
{
|
|
pfs = PfsForPfi(pad, pad->iedCur, pfi);
|
|
|
|
PthForUFile(pad, pfi, pthUser);
|
|
fCanStat = FPthExists(pthUser, (pfi->fk==fkDir));
|
|
|
|
switch(pfs->fm)
|
|
{
|
|
default:
|
|
FatalError(szBadFileFormat, pad, pfi);
|
|
case fmIn:
|
|
if (pfi->fk == fkDir)
|
|
{
|
|
if (!fCanStat &&
|
|
FQueryFix("does not exist","create directory", pad, pfi))
|
|
{
|
|
/* create dir and RC file */
|
|
FMkPth(pthUser, (void *)0, fFalse);
|
|
CreateRc(pad, pfi);
|
|
}
|
|
break;
|
|
}
|
|
case fmAdd:
|
|
if (pfi->fk == fkDir)
|
|
{
|
|
if (!fCanStat &&
|
|
FQueryFix("(directory) is to be added", "add now", pad, pfi))
|
|
{
|
|
/* create dir and RC file */
|
|
FMkPth(pthUser, (void *)0, fFalse);
|
|
CreateRc(pad, pfi);
|
|
pfs->fm = fmIn;
|
|
}
|
|
break;
|
|
}
|
|
case fmOut:
|
|
case fmGhost:
|
|
case fmNonExistent:
|
|
case fmDelIn:
|
|
case fmDelOut:
|
|
case fmCopyIn:
|
|
case fmMerge:
|
|
case fmVerify:
|
|
case fmConflict:
|
|
break;
|
|
} /* switch */
|
|
} /* main for loop for FS check */
|
|
}
|
|
|
|
|
|
/* Quick and dirty set type. */
|
|
typedef unsigned SET;
|
|
|
|
#define EmptySet(set) ((set) = 0)
|
|
#define AddSet(set,w) ((set) |= (1 << (w)))
|
|
#define FInSet(set,w) (((set) & (1 << (w))) != 0)
|
|
#define FSetEmpty(set) ((set) == 0)
|
|
|
|
/* This function checks the rc file for the directory in question. It is
|
|
* basically an expanded version of FLoadRc, with error messages and queries
|
|
* added.
|
|
*/
|
|
void
|
|
CheckRc(
|
|
AD *pad)
|
|
{
|
|
MF *pmf;
|
|
char *pch, *sz;
|
|
PTH pthRc[cchPthMax];
|
|
SET setErrs;
|
|
int isz;
|
|
static char *rgszErrs[] =
|
|
{
|
|
"RC file %!s does not specify project\n",
|
|
"RC file %!s gives incorrect project\n",
|
|
"RC file %!s does not specify SLM root\n",
|
|
"RC file %!s gives incorrect SLM root\n",
|
|
"RC file %!s does not specify user root\n",
|
|
"RC file %!s gives incorrect user root\n",
|
|
"RC file %!s does not specify subdir\n",
|
|
"RC file %!s gives incorrect subdir\n",
|
|
0
|
|
};
|
|
|
|
/* 4 lines with at most 12 bytes of text; 3 lines with pth; 1 with
|
|
project; 4 crlf. About 350 bytes.
|
|
*/
|
|
#define cbRcMax (4*12+3*cchPthMax+cchProjMax+4*2)
|
|
|
|
char rgbRc[cbRcMax+1];
|
|
|
|
AssertLoaded(pad);
|
|
|
|
SzPrint(pthRc, "%&/U/Q/R", pad);
|
|
|
|
if (fVerbose)
|
|
PrErr("Checking RC file %!s\n", pthRc);
|
|
|
|
/* Check the file here. It is simplest to call CreateRc if there is a
|
|
* problem and the user gives us permission.
|
|
*/
|
|
if ((pmf = PmfOpen(pthRc, omReadOnly, fxNil)) == 0)
|
|
{
|
|
if (FQueryRc("cannot open RC file %!s", pthRc))
|
|
CreateRc(pad, (FI far *)0);
|
|
return;
|
|
}
|
|
|
|
/* read and terminate */
|
|
rgbRc[CbReadMf(pmf, (char far *)rgbRc, cbRcMax)] = '\0';
|
|
CloseMf(pmf);
|
|
|
|
pch = rgbRc;
|
|
|
|
/* Build a set of problems with the file. */
|
|
EmptySet(setErrs);
|
|
if (!FScanLn(&pch, "project = ", &sz, cchProjMax))
|
|
AddSet(setErrs, 0);
|
|
else if (NmCmpSz(pad->nmProj, sz, cchProjMax) != 0)
|
|
AddSet(setErrs, 1);
|
|
|
|
if (!FScanLn(&pch, "slm root = ", &sz, cchPthMax - 1))
|
|
AddSet(setErrs, 2);
|
|
else if (PthCmp(pad->pthSRoot, sz) != 0)
|
|
AddSet(setErrs, 3);
|
|
|
|
if (!FScanLn(&pch, "user root = ", &sz, cchPthMax - 1))
|
|
AddSet(setErrs, 4);
|
|
else if (PthCmp(pad->pthURoot, sz) != 0)
|
|
AddSet(setErrs, 5);
|
|
|
|
if (!FScanLn(&pch, "sub dir = ", &sz, cchPthMax - 1))
|
|
AddSet(setErrs, 6);
|
|
else if (PthCmp(pad->pthSSubDir, sz) != 0)
|
|
AddSet(setErrs, 7);
|
|
|
|
/* Complain about the set of problems. */
|
|
for (isz = 0; rgszErrs[isz] != 0; isz++)
|
|
if (FInSet(setErrs, isz))
|
|
Error(rgszErrs[isz], pthRc);
|
|
|
|
if (!FSetEmpty(setErrs) && FQueryRc("RC file %!s is broken", pthRc))
|
|
CreateRc(pad, (FI far *)0);
|
|
}
|
|
|
|
|
|
/*VARARGS1*/
|
|
F
|
|
FQueryRc(
|
|
char *sz, ...)
|
|
{
|
|
va_list ap;
|
|
F f;
|
|
|
|
va_start(ap, sz);
|
|
f = VaFQueryApp(sz, "write correct file", ap);
|
|
va_end(ap);
|
|
|
|
return f;
|
|
}
|
|
|
|
|
|
/* Here we check the ED. This involves verifying that we have a iedCur
|
|
* and that the nmOwner is correct. If not we do our best to fix it.
|
|
*/
|
|
F
|
|
FCkEdUser(
|
|
AD *pad)
|
|
{
|
|
AssertLoaded(pad);
|
|
|
|
CheckForBreak();
|
|
|
|
/* First we determine iedCur if FLoadEd couldn't, i.e. no pthEd is
|
|
* correct. In this case we use nmOwner and try for a match.
|
|
*/
|
|
if (pad->iedCur == iedNil)
|
|
{
|
|
IED ied;
|
|
|
|
/* need to be able to query */
|
|
if (!FCanQuery(szNotEnlisted, pad, pad, pad, pad))
|
|
return fFalse;
|
|
|
|
for (ied = 0; ied < pad->psh->iedMac; ied++)
|
|
{
|
|
if ((!FIsFreeEdValid(pad->psh) || !pad->rged[ied].fFreeEd) &&
|
|
NmCmp(pad->rged[ied].nmOwner, pad->nmInvoker, cchUserMax) == 0)
|
|
{
|
|
if (pad->flags & flagCkIgnDrive)
|
|
{
|
|
if (NmCmp(pad->rged[ied].pthEd+4, pad->pthURoot+4, cchPthMax-4) != 0)
|
|
continue;
|
|
}
|
|
|
|
if (FQueryApp("path for %&O is %&/E", "change to current directory", pad, ied, pad, ied))
|
|
{
|
|
pad->iedCur = ied;
|
|
ClearLpbCb(pad->rged[ied].pthEd,
|
|
sizeof(pad->rged[ied].pthEd));
|
|
PthCopy(pad->rged[ied].pthEd, pad->pthURoot);
|
|
return fTrue;
|
|
}
|
|
}
|
|
}
|
|
Error(szNotEnlisted, pad, pad, pad, pad);
|
|
return fFalse;
|
|
}
|
|
|
|
else if (NmCmp(pad->rged[pad->iedCur].nmOwner, pad->nmInvoker, cchUserMax) != 0 &&
|
|
FQueryApp("owner for %!&/U/Q is %&O", "change to invokers name", pad, pad, pad->iedCur))
|
|
NmCopy(pad->rged[pad->iedCur].nmOwner, pad->nmInvoker, cchUserMax);
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/* This function checks the contents of the FS associated with the user's
|
|
* ED against the actual contents of the enlisted directory.
|
|
* At this point, we assume the FS is correct, especially with regards to
|
|
* deleted vs. nondeleted, and any mistakes are due to the user's directory.
|
|
*/
|
|
void
|
|
CkFsUser(
|
|
AD *pad)
|
|
{
|
|
register FS far *pfs;
|
|
register FI far *pfi;
|
|
FI far *pfiMac;
|
|
struct _stat st;
|
|
F fReadOnly;
|
|
F fSameFile;
|
|
F fCanStat;
|
|
FM fmGuess;
|
|
PTH pthSFile[cchPthMax];
|
|
PTH pthUser[cchPthMax];
|
|
|
|
AssertLoaded(pad);
|
|
|
|
for (pfi = pad->rgfi, pfiMac = pfi + pad->psh->ifiMac; pfi < pfiMac; pfi++)
|
|
{
|
|
CheckForBreak();
|
|
|
|
pfs = PfsForPfi(pad, pad->iedCur, pfi);
|
|
|
|
PthForUFile(pad, pfi, pthUser);
|
|
if (fVerbose)
|
|
PrErr("Checking %!s\n", pthUser);
|
|
|
|
fCanStat = FStatPth(pthUser, &st);
|
|
if (fCanStat)
|
|
{
|
|
if (((st.st_mode & S_IFDIR) == S_IFDIR) !=
|
|
(pfi->fk == fkDir))
|
|
{
|
|
Error("%&C/F should be %s; remove or rename and run ssync\n", pad, pfi, (pfi->fk == fkDir) ? "directory" : "file");
|
|
break;
|
|
}
|
|
fReadOnly = FReadOnly(&st);
|
|
}
|
|
|
|
if (pfi->fDeleted)
|
|
{
|
|
/* translate non-deleted modes to deleted */
|
|
fmGuess = FmMapFm(pfs->fm, mpNonDelToDel);
|
|
}
|
|
else
|
|
{
|
|
/* translate deleted modes to non-deleted */
|
|
fmGuess = FmMapFm(pfs->fm, mpDelToNonDel);
|
|
}
|
|
if (pfs->fm != fmGuess &&
|
|
FQueryFix("%s the project, yet mode is opposite",
|
|
"change mode", pad, pfi, pfi->fDeleted
|
|
? "has been deleted from" : "is still in"))
|
|
pfs->fm = fmGuess;
|
|
|
|
if (pfi->fk == fkDir)
|
|
{
|
|
/* translate non-dir modes to dir */
|
|
fmGuess = FmMapFm(pfs->fm, mpNonDirToDir);
|
|
}
|
|
if (pfs->fm != fmGuess &&
|
|
FQueryFix("is a directory, yet current mode improper for a directory",
|
|
"change mode", pad, pfi))
|
|
pfs->fm = fmGuess;
|
|
|
|
switch(pfs->fm)
|
|
{
|
|
default:
|
|
FatalError(szBadFileFormat, pad, pfi);
|
|
case fmNonExistent:
|
|
if (!fCanStat || pfi->fk == fkDir)
|
|
break;
|
|
|
|
/* fm is del but file exists, believe should be del */
|
|
if (FQueryFix("has been deleted from project", "is it a private version", pad, pfi))
|
|
/* file with coincident name as deleted file */
|
|
break;
|
|
|
|
if (fReadOnly)
|
|
{
|
|
/* file is readonly so fm should probably be in-del */
|
|
if (FQueryFix("is readonly, but src file has been deleted", "change to del(in)", pad, pfi))
|
|
pfs->fm = fmDelIn;
|
|
break;
|
|
}
|
|
|
|
/* file is r/w so fm probably out-del */
|
|
else if (FQueryFix("is writeable, but src file has been deleted", "change to del(out)", pad, pfi))
|
|
{
|
|
pfs->fm = fmDelOut;
|
|
}
|
|
break;
|
|
case fmIn:
|
|
if (!fCanStat)
|
|
{
|
|
if (pfi->fk != fkDir )
|
|
{
|
|
Error("%&C/F does not exist; run ssync to get new copy\n", pad, pfi);
|
|
break;
|
|
}
|
|
|
|
if (FQueryFix("does not exist","create directory", pad, pfi))
|
|
{
|
|
/* create dir and RC file */
|
|
FMkPth(pthUser, (void *)0, fFalse);
|
|
CreateRc(pad, pfi);
|
|
}
|
|
break;
|
|
}
|
|
|
|
/* check directory permissions */
|
|
if (pfi->fk == fkDir)
|
|
{
|
|
FCkWritePth(pthUser, &st);
|
|
break;
|
|
}
|
|
|
|
PthForCachedSFile(pad, pfi, pthSFile);
|
|
fSameFile = FSameFile(pthUser, pthSFile) || (pfi->fk == fkVersion);
|
|
|
|
if (fReadOnly && fSameFile)
|
|
break;
|
|
|
|
if (fReadOnly)
|
|
{
|
|
if (FQueryFix("is readonly but differs from src file", "change to update", pad, pfi))
|
|
pfs->fm = fmCopyIn;
|
|
break;
|
|
}
|
|
|
|
if (fSameFile &&
|
|
FQueryFix("is writeable and identical to src file",
|
|
"change mode to readonly", pad, pfi))
|
|
{
|
|
SetROPth(pthUser, fTrue, fxLocal);
|
|
}
|
|
else if (!fSameFile &&
|
|
FQueryFix("is writeable and differs from src file", "change to checked out", pad, pfi))
|
|
{
|
|
/* File should be checked out. */
|
|
pfs->fm = fmOut;
|
|
}
|
|
else
|
|
Error("run ssync to recover copy\n");
|
|
break;
|
|
case fmOut:
|
|
case fmVerify:
|
|
case fmConflict:
|
|
if (pfi->fk == fkDir)
|
|
{
|
|
/* REVIEW: this only happens if user answered no
|
|
* to remap query above.
|
|
*/
|
|
break;
|
|
}
|
|
|
|
if (!fCanStat)
|
|
{
|
|
Error("%&C/F does not exist; run out -c to recover copy\n", pad, pfi);
|
|
break;
|
|
}
|
|
if (!fReadOnly)
|
|
break; /* mode looks right */
|
|
|
|
/* file is readonly */
|
|
fSameFile = FSameFile(pthUser, PthForCachedSFile(pad, pfi, pthSFile));
|
|
if (fSameFile &&
|
|
FQueryFix("is checked out, identical to src file",
|
|
"change to checked in", pad, pfi))
|
|
/* File should be checked in. */
|
|
pfs->fm = fmIn;
|
|
else if (!fSameFile &&
|
|
FQueryFix("is checked out, readonly and differs from src file", "change to update", pad, pfi))
|
|
pfs->fm = fmCopyIn;
|
|
break;
|
|
case fmGhost:
|
|
if (pfi->fk == fkDir)
|
|
{
|
|
/* REVIEW: this only happens if user answered no
|
|
* to remap query above.
|
|
*/
|
|
break;
|
|
}
|
|
|
|
if (!fCanStat)
|
|
Warn("%&C/F exists but is ghosted\n", pad, pfi);
|
|
|
|
else /* fCanStat */
|
|
{
|
|
fSameFile = FSameFile(pthUser, PthForCachedSFile(pad, pfi, pthSFile));
|
|
if (fReadOnly)
|
|
{
|
|
if (fSameFile &&
|
|
FQueryFix("is ghosted, but is readonly and is identical to src file", "change to checked in", pad, pfi))
|
|
/* File should be checked in. */
|
|
pfs->fm = fmIn;
|
|
else if (!fSameFile)
|
|
Warn("%&C/F is ghosted, but is readonly and differs from src file\n", pad, pfi);
|
|
}
|
|
|
|
else
|
|
{
|
|
if (fSameFile &&
|
|
FQueryFix("is ghosted, but is writeable and identical to src file", "change to checked in (and readonly)", pad, pfi))
|
|
{
|
|
SetROPth(pthUser, fTrue, fxLocal);
|
|
pfs->fm = fmIn;
|
|
}
|
|
else if (!fSameFile)
|
|
Warn("%&C/F is ghosted, but is writable and differs from src file\n", pad, pfi);
|
|
else
|
|
Error("run ssync to get new file\n");
|
|
}
|
|
}
|
|
break;
|
|
case fmAdd:
|
|
if (pfi->fk == fkDir)
|
|
{
|
|
if (!fCanStat &&
|
|
FQueryFix("(directory) is to be added", "add now", pad, pfi))
|
|
{
|
|
/* create dir and RC file */
|
|
FMkPth(pthUser, (void *)0, fFalse);
|
|
CreateRc(pad, pfi);
|
|
pfs->fm = fmIn;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (!fCanStat)
|
|
break;
|
|
|
|
fSameFile = FSameFile(pthUser, PthForCachedSFile(pad, pfi, pthSFile));
|
|
if (fReadOnly)
|
|
{
|
|
if (fSameFile &&
|
|
FQueryFix("is to be added, is readonly and is identical to src file", "change to checked in", pad, pfi))
|
|
/* File should be checked in. */
|
|
pfs->fm = fmIn;
|
|
else if (!fSameFile &&
|
|
FQueryFix("is to be added, is readonly and differs from src file", "change to copy-in", pad, pfi))
|
|
pfs->fm = fmCopyIn;
|
|
break;
|
|
}
|
|
|
|
else
|
|
{
|
|
if (fSameFile &&
|
|
FQueryFix("is to be added, is writeable and identical to src file", "change to checked in (and readonly)", pad, pfi))
|
|
{
|
|
SetROPth(pthUser, fTrue, fxLocal);
|
|
pfs->fm = fmIn;
|
|
}
|
|
else if (!fSameFile &&
|
|
FQueryFix("is to be added, is writeable and differs from src file", "change to checked out", pad, pfi))
|
|
/* File should be checked out. */
|
|
pfs->fm = fmOut;
|
|
else
|
|
Error("run ssync to get new file\n");
|
|
}
|
|
break;
|
|
case fmDelIn:
|
|
if (!fCanStat)
|
|
{
|
|
if (FQueryFix("does not exist", "change to deleted", pad, pfi))
|
|
pfs->fm = fmNonExistent;
|
|
break;
|
|
}
|
|
if (pfi->fk == fkDir)
|
|
{
|
|
FCkWritePth(pthUser, &st);
|
|
break;
|
|
}
|
|
if (fReadOnly)
|
|
break; /* no problems */
|
|
|
|
/* r/w */
|
|
else if (FQueryFix("is to be deleted(in) and is writeable", "change to del(out)", pad, pfi))
|
|
pfs->fm = fmDelOut;
|
|
break;
|
|
|
|
case fmDelOut:
|
|
if (pfi->fk == fkDir)
|
|
{
|
|
/* REVIEW: this only happens if user answered no
|
|
* to remap query above.
|
|
*/
|
|
break;
|
|
}
|
|
|
|
if (!fCanStat)
|
|
{
|
|
if (FQueryFix("does not exist", "change to deleted", pad, pfi))
|
|
pfs->fm = fmNonExistent;
|
|
break;
|
|
}
|
|
if (!fReadOnly)
|
|
break; /* no problems */
|
|
|
|
if (FQueryFix("is to be deleted(out) and is readonly", "change to del(in)", pad, pfi))
|
|
pfs->fm = fmDelIn;
|
|
break;
|
|
case fmCopyIn:
|
|
if (pfi->fk == fkDir)
|
|
{
|
|
/* REVIEW: this only happens if user answered no
|
|
* to remap query above.
|
|
*/
|
|
break;
|
|
}
|
|
|
|
if (!fCanStat)
|
|
{
|
|
Error("%&C/F does not exist; run ssync for new copy\n", pad, pfi);
|
|
break;
|
|
}
|
|
if (fReadOnly)
|
|
break; /* mode looks right */
|
|
|
|
if (FQueryFix("is to be updated and is writeable", "change to checked out", pad, pfi))
|
|
{
|
|
pfs->fm = fmOut;
|
|
Warn("relationship of %&C/F to source pool is unknown\n", pad, pfi);
|
|
}
|
|
|
|
break;
|
|
case fmMerge:
|
|
if (pfi->fk == fkDir)
|
|
{
|
|
/* REVIEW: this only happens if user answered no
|
|
* to remap query above.
|
|
*/
|
|
break;
|
|
}
|
|
|
|
if (!fCanStat)
|
|
{
|
|
Error("%&C/F does not exist; run out -c to get new copy\n", pad, pfi);
|
|
break;
|
|
}
|
|
if (!fReadOnly)
|
|
break; /* mode looks right */
|
|
|
|
/* file is readonly */
|
|
fSameFile = FSameFile(pthUser, PthForCachedSFile(pad, pfi, pthSFile));
|
|
|
|
if (fSameFile &&
|
|
FQueryFix("is to be merged, is readonly and is identical to src file", "change to checked in", pad, pfi))
|
|
{
|
|
/* File should be checked in. */
|
|
pfs->fm = fmIn;
|
|
DelBase(pad, pfi, pfs);
|
|
}
|
|
else if (!fSameFile &&
|
|
FQueryFix("is to be merged, is readonly and %s src file", "change to copy-in",
|
|
pad, pfi, fSameFile ? "is identical to" : "differs from"))
|
|
{
|
|
pfs->fm = fmCopyIn;
|
|
DelBase(pad, pfi, pfs);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*VARARGS4*/
|
|
/* the args can only refer to szProblem */
|
|
F
|
|
FQueryFix(
|
|
char *szProblem,
|
|
char *szFix,
|
|
AD *pad,
|
|
FI far *pfi,
|
|
...)
|
|
{
|
|
char sz1[cchMsgMax];
|
|
va_list ap;
|
|
|
|
va_start(ap, pfi);
|
|
VaSzPrint(sz1, szProblem, ap);
|
|
va_end(ap);
|
|
|
|
return FQueryApp("%&C/F %s", szFix, pad, pfi, sz1);
|
|
}
|