From 765bb012efa7d31d7af32135212233ba2b563d18 Mon Sep 17 00:00:00 2001 From: David Goodwin Date: Tue, 18 Nov 2025 18:56:25 +1300 Subject: [PATCH] This fixes a problem with CLEAR APC that results in it defeating all APC safety checks. --- ckuus2.c | 2 +- ckuus4.c | 5 ++++- ckuus5.c | 1 + ckuusr.c | 19 +++++++++++++++++-- 4 files changed, 23 insertions(+), 4 deletions(-) diff --git a/ckuus2.c b/ckuus2.c index 3e8bd45..289e8b5 100644 --- a/ckuus2.c +++ b/ckuus2.c @@ -3102,7 +3102,7 @@ static char * hmxxcle[] = { " ", " ALARM Clears any pending alarm (see SET ALARM).", #ifdef CK_APC -" APC-STATUS Clears Application Program Command status.", +" APC Clears Application Program Command status.", #endif /* CK_APC */ #ifdef PATTERNS " BINARY-PATTERNS Clears the file binary-patterns list.", diff --git a/ckuus4.c b/ckuus4.c index dc65d39..39c9d4b 100644 --- a/ckuus4.c +++ b/ckuus4.c @@ -356,6 +356,7 @@ int isjoin = 0; #ifdef CK_APC extern int apcactive; /* Nonzero = APC command was rec'd */ extern int apcstatus; /* Are APC commands being processed? */ +extern int apccmd; /* Stay on command screen after APC? */ #ifdef DCMDBUF extern char *apcbuf; /* APC command buffer */ #else @@ -2354,6 +2355,7 @@ doconect(q,async) int q, async; (apcactive == APC_REMOTE && apcstatus != APC_OFF))) { debug(F101,"doconect justone 3","",justone); if (mlook(mactab,"_apc_commands",nmac) == -1) { + apccmd = 0; /* So we can catch any CLEAR APC */ debug(F110,"doconect about to execute APC",apcbuf,0); domac("_apc_commands",apcbuf,cmdstk[cmdlvl].ccflgs|CF_APC); delmac("_apc_commands",1); @@ -2363,7 +2365,8 @@ doconect(q,async) int q, async; #endif /* DEBUG */ } debug(F101,"doconect apcactive after domac","",apcactive); - if (!apcactive) { /* In case CLEAR APC was in APC */ + if (!apcactive || apccmd) { /* In case CLEAR APC was in APC */ + apcactive = APC_INACTIVE; debug(F101,"doconect quit APC loop: apcactive","",apcactive); break; } diff --git a/ckuus5.c b/ckuus5.c index fdd286b..a231cee 100644 --- a/ckuus5.c +++ b/ckuus5.c @@ -849,6 +849,7 @@ int timelimit = 0, asktimer = 0; /* Timers for time-limited commands */ #ifdef CK_APC /* Application Program Command (APC) */ int apcactive = APC_INACTIVE; int apcstatus = APC_OFF; /* OFF by default everywhere */ +int apccmd = 0; /* Remain on command screen after APC? */ #ifdef DCMDBUF char *apcbuf; #else diff --git a/ckuusr.c b/ckuusr.c index 36462dd..222e710 100644 --- a/ckuusr.c +++ b/ckuusr.c @@ -404,7 +404,7 @@ extern int dialsta, dialatmo, dialcon, dialcq; /* DIAL status, etc. */ #endif /* NODIAL */ #ifdef CK_APC -extern int apcactive, apcstatus; +extern int apcactive, apcstatus, apccmd; #endif /* CK_APC */ #ifndef NOPUSH @@ -6735,7 +6735,22 @@ doclear() { #ifdef CK_APC if (x & CLR_APC) { debug(F101,"Executing CLEAR APC","",apcactive); - apcactive = 0; +#ifndef OS2 + if (apcactive == APC_LOCAL || + (apcactive == APC_REMOTE && (apcstatus & APC_UNCH))) { + /* Clearing the APC status like this mid-APC is equivalent to being allowed to + * do SET TERM APC UNCHECKED mid-APC. If Kermit doesn't think an APC is active, + * it doesn't check if commands are safe to appear in an APC. Additionally, in + * Kermit 95 1.1.21 and up forcing APC inactive like this mid-APC causes the + * parser to get stuck waiting forever for a semaphore that has already been + * raised. To the user it looks like a crash. */ + apcactive = APC_INACTIVE; + } +#endif /* OS2 */ + /* The documented purpose of this command is to cause Kermit to + * remain on the command screen when the APC is finished. + * This is a safer way to do it. */ + apccmd = 1; y = 0; } #endif /* CK_APC */