Re: Atomic signal flags for vi(1)

2021-09-02 Thread Martijn van Duren
On Thu, 2021-09-02 at 13:25 +0200, Ingo Schwarze wrote:
> Hi Tim,
> 
> trondd wrote on Wed, Sep 01, 2021 at 08:46:28PM -0400:
> > Ingo Schwarze  wrote:
> > > Ingo Schwarze wrote on Wed, Sep 01, 2021 at 04:38:51PM +0200:
> 
> > > > Note that the h_hup() and h_term() signal handlers are still unsafe
> > > > after this commit because they also set the "killersig" (how fitting!)
> > > > field in a global struct.
> 
> > > I like it when fixing two bugs only amounts to minus: minus 14 LOC,
> > > 1 function, 1 global variable, 2 automatic variables, 1 struct member,
> > > 9 pointer dereferences, 1 #define, and minus 1 #undef.
> > > 
> > > The argument that only one single GS exists applies unchanged.
> 
> > Great.  This is the direction I was going to go in with it.  I hadn't
> > thought of collapsing h_hup an h_term, though.
> > 
> > This makes sense as I understand the signal/event handling and a quick
> > test through the signals shows no difference in behavior.
> 
> Thank you (and martijn@) for reviewing and testing.
> This is now committed.
> 
> > Should h_term() and cl_sigterm be named something more general to
> > indicate that they also handle SIGHUP?  Or is it good enough since it
> > includes all the signals that 'term'inate vi?  It's not hard to follow
> > as-is. 
> 
> That is what i was thinking: why re-paint a bike shed if the existing
> paint is already pretty and not yet falling off?

Luckily the paint is pretty. :-)
> 
> Yours,
>   Ingo
> 
> 
> > > Index: cl/cl.h
> > > ===
> > > RCS file: /cvs/src/usr.bin/vi/cl/cl.h,v
> > > retrieving revision 1.11
> > > diff -u -p -r1.11 cl.h
> > > --- cl/cl.h   1 Sep 2021 14:28:15 -   1.11
> > > +++ cl/cl.h   1 Sep 2021 17:15:34 -
> > > @@ -11,7 +11,6 @@
> > >   *   @(#)cl.h10.19 (Berkeley) 9/24/96
> > >   */
> > >  
> > > -extern   volatile sig_atomic_t cl_sighup;
> > >  extern   volatile sig_atomic_t cl_sigint;
> > >  extern   volatile sig_atomic_t cl_sigterm;
> > >  extern   volatile sig_atomic_t cl_sigwinch;
> > > @@ -31,7 +30,6 @@ typedef struct _cl_private {
> > >   char*rmso, *smso;   /* Inverse video terminal strings. */
> > >   char*smcup, *rmcup; /* Terminal start/stop strings. */
> > >  
> > > - int  killersig; /* Killer signal. */
> > >  #define  INDX_HUP0
> > >  #define  INDX_INT1
> > >  #define  INDX_TERM   2
> > > Index: cl/cl_funcs.c
> > > ===
> > > RCS file: /cvs/src/usr.bin/vi/cl/cl_funcs.c,v
> > > retrieving revision 1.21
> > > diff -u -p -r1.21 cl_funcs.c
> > > --- cl/cl_funcs.c 1 Sep 2021 14:28:15 -   1.21
> > > +++ cl/cl_funcs.c 1 Sep 2021 17:15:34 -
> > > @@ -437,7 +437,7 @@ cl_refresh(SCR *sp, int repaint)
> > >* If we received a killer signal, we're done, there's no point
> > >* in refreshing the screen.
> > >*/
> > > - if (clp->killersig)
> > > + if (cl_sigterm)
> > >   return (0);
> > >  
> > >   /*
> > > @@ -582,7 +582,7 @@ cl_suspend(SCR *sp, int *allowedp)
> > >* unchanged.  In addition, the terminal has already been reset
> > >* correctly, so leave it alone.
> > >*/
> > > - if (clp->killersig) {
> > > + if (cl_sigterm) {
> > >   F_CLR(clp, CL_SCR_EX_INIT | CL_SCR_VI_INIT);
> > >   return (0);
> > >   }
> > > Index: cl/cl_main.c
> > > ===
> > > RCS file: /cvs/src/usr.bin/vi/cl/cl_main.c,v
> > > retrieving revision 1.34
> > > diff -u -p -r1.34 cl_main.c
> > > --- cl/cl_main.c  1 Sep 2021 14:28:15 -   1.34
> > > +++ cl/cl_main.c  1 Sep 2021 17:15:34 -
> > > @@ -33,7 +33,6 @@
> > >  
> > >  GS *__global_list;   /* GLOBAL: List of 
> > > screens. */
> > >  
> > > -volatile sig_atomic_t cl_sighup;
> > >  volatile sig_atomic_t cl_sigint;
> > >  volatile sig_atomic_t cl_sigterm;
> > >  volatile sig_atomic_t cl_sigwinch;
> > > @@ -120,9 +119,9 @@ main(int argc, char *argv[])
> > >   }
> > >  
> > >   /* If a killer signal arrived, pretend we just got it. */
> > > - if (clp->killersig) {
> > > - (void)signal(clp->killersig, SIG_DFL);
> > > - (void)kill(getpid(), clp->killersig);
> > > + if (cl_sigterm) {
> > > + (void)signal(cl_sigterm, SIG_DFL);
> > > + (void)kill(getpid(), cl_sigterm);
> > >   /* NOTREACHED */
> > >   }
> > >  
> > > @@ -215,17 +214,6 @@ term_init(char *ttype)
> > >   }
> > >  }
> > >  
> > > -#define  GLOBAL_CLP \
> > > - CL_PRIVATE *clp = GCLP(__global_list);
> > > -static void
> > > -h_hup(int signo)
> > > -{
> > > - GLOBAL_CLP;
> > > -
> > > - cl_sighup = 1;
> > > - clp->killersig = SIGHUP;
> > > -}
> > > -
> > >  static void
> > >  h_int(int signo)
> > >  {
> > > @@ -235,10 +223,7 @@ h_int(int signo)
> > >  static void
> > >  h_term(int signo)
> > >  {
> > > - GLOBAL_CLP;
> > > -
> > > - cl_sigterm = 1;
> > > - 

Re: Atomic signal flags for vi(1)

2021-09-02 Thread Ingo Schwarze
Hi Tim,

trondd wrote on Wed, Sep 01, 2021 at 08:46:28PM -0400:
> Ingo Schwarze  wrote:
>> Ingo Schwarze wrote on Wed, Sep 01, 2021 at 04:38:51PM +0200:

>>> Note that the h_hup() and h_term() signal handlers are still unsafe
>>> after this commit because they also set the "killersig" (how fitting!)
>>> field in a global struct.

>> I like it when fixing two bugs only amounts to minus: minus 14 LOC,
>> 1 function, 1 global variable, 2 automatic variables, 1 struct member,
>> 9 pointer dereferences, 1 #define, and minus 1 #undef.
>> 
>> The argument that only one single GS exists applies unchanged.

> Great.  This is the direction I was going to go in with it.  I hadn't
> thought of collapsing h_hup an h_term, though.
> 
> This makes sense as I understand the signal/event handling and a quick
> test through the signals shows no difference in behavior.

Thank you (and martijn@) for reviewing and testing.
This is now committed.

> Should h_term() and cl_sigterm be named something more general to
> indicate that they also handle SIGHUP?  Or is it good enough since it
> includes all the signals that 'term'inate vi?  It's not hard to follow
> as-is. 

That is what i was thinking: why re-paint a bike shed if the existing
paint is already pretty and not yet falling off?

Yours,
  Ingo


>> Index: cl/cl.h
>> ===
>> RCS file: /cvs/src/usr.bin/vi/cl/cl.h,v
>> retrieving revision 1.11
>> diff -u -p -r1.11 cl.h
>> --- cl/cl.h  1 Sep 2021 14:28:15 -   1.11
>> +++ cl/cl.h  1 Sep 2021 17:15:34 -
>> @@ -11,7 +11,6 @@
>>   *  @(#)cl.h10.19 (Berkeley) 9/24/96
>>   */
>>  
>> -extern  volatile sig_atomic_t cl_sighup;
>>  extern  volatile sig_atomic_t cl_sigint;
>>  extern  volatile sig_atomic_t cl_sigterm;
>>  extern  volatile sig_atomic_t cl_sigwinch;
>> @@ -31,7 +30,6 @@ typedef struct _cl_private {
>>  char*rmso, *smso;   /* Inverse video terminal strings. */
>>  char*smcup, *rmcup; /* Terminal start/stop strings. */
>>  
>> -int  killersig; /* Killer signal. */
>>  #define INDX_HUP0
>>  #define INDX_INT1
>>  #define INDX_TERM   2
>> Index: cl/cl_funcs.c
>> ===
>> RCS file: /cvs/src/usr.bin/vi/cl/cl_funcs.c,v
>> retrieving revision 1.21
>> diff -u -p -r1.21 cl_funcs.c
>> --- cl/cl_funcs.c1 Sep 2021 14:28:15 -   1.21
>> +++ cl/cl_funcs.c1 Sep 2021 17:15:34 -
>> @@ -437,7 +437,7 @@ cl_refresh(SCR *sp, int repaint)
>>   * If we received a killer signal, we're done, there's no point
>>   * in refreshing the screen.
>>   */
>> -if (clp->killersig)
>> +if (cl_sigterm)
>>  return (0);
>>  
>>  /*
>> @@ -582,7 +582,7 @@ cl_suspend(SCR *sp, int *allowedp)
>>   * unchanged.  In addition, the terminal has already been reset
>>   * correctly, so leave it alone.
>>   */
>> -if (clp->killersig) {
>> +if (cl_sigterm) {
>>  F_CLR(clp, CL_SCR_EX_INIT | CL_SCR_VI_INIT);
>>  return (0);
>>  }
>> Index: cl/cl_main.c
>> ===
>> RCS file: /cvs/src/usr.bin/vi/cl/cl_main.c,v
>> retrieving revision 1.34
>> diff -u -p -r1.34 cl_main.c
>> --- cl/cl_main.c 1 Sep 2021 14:28:15 -   1.34
>> +++ cl/cl_main.c 1 Sep 2021 17:15:34 -
>> @@ -33,7 +33,6 @@
>>  
>>  GS *__global_list;  /* GLOBAL: List of screens. */
>>  
>> -volatile sig_atomic_t cl_sighup;
>>  volatile sig_atomic_t cl_sigint;
>>  volatile sig_atomic_t cl_sigterm;
>>  volatile sig_atomic_t cl_sigwinch;
>> @@ -120,9 +119,9 @@ main(int argc, char *argv[])
>>  }
>>  
>>  /* If a killer signal arrived, pretend we just got it. */
>> -if (clp->killersig) {
>> -(void)signal(clp->killersig, SIG_DFL);
>> -(void)kill(getpid(), clp->killersig);
>> +if (cl_sigterm) {
>> +(void)signal(cl_sigterm, SIG_DFL);
>> +(void)kill(getpid(), cl_sigterm);
>>  /* NOTREACHED */
>>  }
>>  
>> @@ -215,17 +214,6 @@ term_init(char *ttype)
>>  }
>>  }
>>  
>> -#define GLOBAL_CLP \
>> -CL_PRIVATE *clp = GCLP(__global_list);
>> -static void
>> -h_hup(int signo)
>> -{
>> -GLOBAL_CLP;
>> -
>> -cl_sighup = 1;
>> -clp->killersig = SIGHUP;
>> -}
>> -
>>  static void
>>  h_int(int signo)
>>  {
>> @@ -235,10 +223,7 @@ h_int(int signo)
>>  static void
>>  h_term(int signo)
>>  {
>> -GLOBAL_CLP;
>> -
>> -cl_sigterm = 1;
>> -clp->killersig = SIGTERM;
>> +cl_sigterm = signo;
>>  }
>>  
>>  static void
>> @@ -246,7 +231,6 @@ h_winch(int signo)
>>  {
>>  cl_sigwinch = 1;
>>  }
>> -#undef  GLOBAL_CLP
>>  
>>  /*
>>   * sig_init --
>> @@ -261,20 +245,19 @@ sig_init(GS *gp, SCR *sp)
>>  
>>  clp = GCLP(gp);
>>  
>> -cl_sighup = 0;
>>  cl_sigint = 0;
>>  cl_sigterm = 0;
>>

Re: Atomic signal flags for vi(1)

2021-09-01 Thread trondd
Ingo Schwarze  wrote:

> Hi,
> 
> Ingo Schwarze wrote on Wed, Sep 01, 2021 at 04:38:51PM +0200:
> 
> > Note that the h_hup() and h_term() signal handlers are still unsafe
> > after this commit because they also set the "killersig" (how fitting!)
> > field in a global struct.
> 
> I like it when fixing two bugs only amounts to minus: minus 14 LOC,
> 1 function, 1 global variable, 2 automatic variables, 1 struct member,
> 9 pointer dereferences, 1 #define, and minus 1 #undef.
> 
> The argument that only one single GS exists applies unchanged.
> 
> OK?
>   Ingo

Great.  This is the direction I was going to go in with it.  I hadn't
thought of collapsing h_hup an h_term, though.

This makes sense as I understand the signal/event handling and a quick
test through the signals shows no difference in behavior.

Should h_term() and cl_sigterm be named something more general to
indicate that they also handle SIGHUP?  Or is it good enough since it
includes all the signals that 'term'inate vi?  It's not hard to follow
as-is. 

Tim. (not @ :P )


> 
> 
> Index: cl/cl.h
> ===
> RCS file: /cvs/src/usr.bin/vi/cl/cl.h,v
> retrieving revision 1.11
> diff -u -p -r1.11 cl.h
> --- cl/cl.h   1 Sep 2021 14:28:15 -   1.11
> +++ cl/cl.h   1 Sep 2021 17:15:34 -
> @@ -11,7 +11,6 @@
>   *   @(#)cl.h10.19 (Berkeley) 9/24/96
>   */
>  
> -extern   volatile sig_atomic_t cl_sighup;
>  extern   volatile sig_atomic_t cl_sigint;
>  extern   volatile sig_atomic_t cl_sigterm;
>  extern   volatile sig_atomic_t cl_sigwinch;
> @@ -31,7 +30,6 @@ typedef struct _cl_private {
>   char*rmso, *smso;   /* Inverse video terminal strings. */
>   char*smcup, *rmcup; /* Terminal start/stop strings. */
>  
> - int  killersig; /* Killer signal. */
>  #define  INDX_HUP0
>  #define  INDX_INT1
>  #define  INDX_TERM   2
> Index: cl/cl_funcs.c
> ===
> RCS file: /cvs/src/usr.bin/vi/cl/cl_funcs.c,v
> retrieving revision 1.21
> diff -u -p -r1.21 cl_funcs.c
> --- cl/cl_funcs.c 1 Sep 2021 14:28:15 -   1.21
> +++ cl/cl_funcs.c 1 Sep 2021 17:15:34 -
> @@ -437,7 +437,7 @@ cl_refresh(SCR *sp, int repaint)
>* If we received a killer signal, we're done, there's no point
>* in refreshing the screen.
>*/
> - if (clp->killersig)
> + if (cl_sigterm)
>   return (0);
>  
>   /*
> @@ -582,7 +582,7 @@ cl_suspend(SCR *sp, int *allowedp)
>* unchanged.  In addition, the terminal has already been reset
>* correctly, so leave it alone.
>*/
> - if (clp->killersig) {
> + if (cl_sigterm) {
>   F_CLR(clp, CL_SCR_EX_INIT | CL_SCR_VI_INIT);
>   return (0);
>   }
> Index: cl/cl_main.c
> ===
> RCS file: /cvs/src/usr.bin/vi/cl/cl_main.c,v
> retrieving revision 1.34
> diff -u -p -r1.34 cl_main.c
> --- cl/cl_main.c  1 Sep 2021 14:28:15 -   1.34
> +++ cl/cl_main.c  1 Sep 2021 17:15:34 -
> @@ -33,7 +33,6 @@
>  
>  GS *__global_list;   /* GLOBAL: List of screens. */
>  
> -volatile sig_atomic_t cl_sighup;
>  volatile sig_atomic_t cl_sigint;
>  volatile sig_atomic_t cl_sigterm;
>  volatile sig_atomic_t cl_sigwinch;
> @@ -120,9 +119,9 @@ main(int argc, char *argv[])
>   }
>  
>   /* If a killer signal arrived, pretend we just got it. */
> - if (clp->killersig) {
> - (void)signal(clp->killersig, SIG_DFL);
> - (void)kill(getpid(), clp->killersig);
> + if (cl_sigterm) {
> + (void)signal(cl_sigterm, SIG_DFL);
> + (void)kill(getpid(), cl_sigterm);
>   /* NOTREACHED */
>   }
>  
> @@ -215,17 +214,6 @@ term_init(char *ttype)
>   }
>  }
>  
> -#define  GLOBAL_CLP \
> - CL_PRIVATE *clp = GCLP(__global_list);
> -static void
> -h_hup(int signo)
> -{
> - GLOBAL_CLP;
> -
> - cl_sighup = 1;
> - clp->killersig = SIGHUP;
> -}
> -
>  static void
>  h_int(int signo)
>  {
> @@ -235,10 +223,7 @@ h_int(int signo)
>  static void
>  h_term(int signo)
>  {
> - GLOBAL_CLP;
> -
> - cl_sigterm = 1;
> - clp->killersig = SIGTERM;
> + cl_sigterm = signo;
>  }
>  
>  static void
> @@ -246,7 +231,6 @@ h_winch(int signo)
>  {
>   cl_sigwinch = 1;
>  }
> -#undef   GLOBAL_CLP
>  
>  /*
>   * sig_init --
> @@ -261,20 +245,19 @@ sig_init(GS *gp, SCR *sp)
>  
>   clp = GCLP(gp);
>  
> - cl_sighup = 0;
>   cl_sigint = 0;
>   cl_sigterm = 0;
>   cl_sigwinch = 0;
>  
>   if (sp == NULL) {
> - if (setsig(SIGHUP, >oact[INDX_HUP], h_hup) ||
> + if (setsig(SIGHUP, >oact[INDX_HUP], h_term) ||
>   setsig(SIGINT, >oact[INDX_INT], h_int) ||
>   setsig(SIGTERM, >oact[INDX_TERM], h_term) ||
> 

Re: Atomic signal flags for vi(1)

2021-09-01 Thread Ingo Schwarze
Hi,

Ingo Schwarze wrote on Wed, Sep 01, 2021 at 04:38:51PM +0200:

> Note that the h_hup() and h_term() signal handlers are still unsafe
> after this commit because they also set the "killersig" (how fitting!)
> field in a global struct.

I like it when fixing two bugs only amounts to minus: minus 14 LOC,
1 function, 1 global variable, 2 automatic variables, 1 struct member,
9 pointer dereferences, 1 #define, and minus 1 #undef.

The argument that only one single GS exists applies unchanged.

OK?
  Ingo


Index: cl/cl.h
===
RCS file: /cvs/src/usr.bin/vi/cl/cl.h,v
retrieving revision 1.11
diff -u -p -r1.11 cl.h
--- cl/cl.h 1 Sep 2021 14:28:15 -   1.11
+++ cl/cl.h 1 Sep 2021 17:15:34 -
@@ -11,7 +11,6 @@
  * @(#)cl.h10.19 (Berkeley) 9/24/96
  */
 
-extern volatile sig_atomic_t cl_sighup;
 extern volatile sig_atomic_t cl_sigint;
 extern volatile sig_atomic_t cl_sigterm;
 extern volatile sig_atomic_t cl_sigwinch;
@@ -31,7 +30,6 @@ typedef struct _cl_private {
char*rmso, *smso;   /* Inverse video terminal strings. */
char*smcup, *rmcup; /* Terminal start/stop strings. */
 
-   int  killersig; /* Killer signal. */
 #defineINDX_HUP0
 #defineINDX_INT1
 #defineINDX_TERM   2
Index: cl/cl_funcs.c
===
RCS file: /cvs/src/usr.bin/vi/cl/cl_funcs.c,v
retrieving revision 1.21
diff -u -p -r1.21 cl_funcs.c
--- cl/cl_funcs.c   1 Sep 2021 14:28:15 -   1.21
+++ cl/cl_funcs.c   1 Sep 2021 17:15:34 -
@@ -437,7 +437,7 @@ cl_refresh(SCR *sp, int repaint)
 * If we received a killer signal, we're done, there's no point
 * in refreshing the screen.
 */
-   if (clp->killersig)
+   if (cl_sigterm)
return (0);
 
/*
@@ -582,7 +582,7 @@ cl_suspend(SCR *sp, int *allowedp)
 * unchanged.  In addition, the terminal has already been reset
 * correctly, so leave it alone.
 */
-   if (clp->killersig) {
+   if (cl_sigterm) {
F_CLR(clp, CL_SCR_EX_INIT | CL_SCR_VI_INIT);
return (0);
}
Index: cl/cl_main.c
===
RCS file: /cvs/src/usr.bin/vi/cl/cl_main.c,v
retrieving revision 1.34
diff -u -p -r1.34 cl_main.c
--- cl/cl_main.c1 Sep 2021 14:28:15 -   1.34
+++ cl/cl_main.c1 Sep 2021 17:15:34 -
@@ -33,7 +33,6 @@
 
 GS *__global_list; /* GLOBAL: List of screens. */
 
-volatile sig_atomic_t cl_sighup;
 volatile sig_atomic_t cl_sigint;
 volatile sig_atomic_t cl_sigterm;
 volatile sig_atomic_t cl_sigwinch;
@@ -120,9 +119,9 @@ main(int argc, char *argv[])
}
 
/* If a killer signal arrived, pretend we just got it. */
-   if (clp->killersig) {
-   (void)signal(clp->killersig, SIG_DFL);
-   (void)kill(getpid(), clp->killersig);
+   if (cl_sigterm) {
+   (void)signal(cl_sigterm, SIG_DFL);
+   (void)kill(getpid(), cl_sigterm);
/* NOTREACHED */
}
 
@@ -215,17 +214,6 @@ term_init(char *ttype)
}
 }
 
-#defineGLOBAL_CLP \
-   CL_PRIVATE *clp = GCLP(__global_list);
-static void
-h_hup(int signo)
-{
-   GLOBAL_CLP;
-
-   cl_sighup = 1;
-   clp->killersig = SIGHUP;
-}
-
 static void
 h_int(int signo)
 {
@@ -235,10 +223,7 @@ h_int(int signo)
 static void
 h_term(int signo)
 {
-   GLOBAL_CLP;
-
-   cl_sigterm = 1;
-   clp->killersig = SIGTERM;
+   cl_sigterm = signo;
 }
 
 static void
@@ -246,7 +231,6 @@ h_winch(int signo)
 {
cl_sigwinch = 1;
 }
-#undef GLOBAL_CLP
 
 /*
  * sig_init --
@@ -261,20 +245,19 @@ sig_init(GS *gp, SCR *sp)
 
clp = GCLP(gp);
 
-   cl_sighup = 0;
cl_sigint = 0;
cl_sigterm = 0;
cl_sigwinch = 0;
 
if (sp == NULL) {
-   if (setsig(SIGHUP, >oact[INDX_HUP], h_hup) ||
+   if (setsig(SIGHUP, >oact[INDX_HUP], h_term) ||
setsig(SIGINT, >oact[INDX_INT], h_int) ||
setsig(SIGTERM, >oact[INDX_TERM], h_term) ||
setsig(SIGWINCH, >oact[INDX_WINCH], h_winch)
)
err(1, NULL);
} else
-   if (setsig(SIGHUP, NULL, h_hup) ||
+   if (setsig(SIGHUP, NULL, h_term) ||
setsig(SIGINT, NULL, h_int) ||
setsig(SIGTERM, NULL, h_term) ||
setsig(SIGWINCH, NULL, h_winch)
Index: cl/cl_read.c
===
RCS file: /cvs/src/usr.bin/vi/cl/cl_read.c,v
retrieving revision 1.22
diff -u -p -r1.22 cl_read.c
--- cl/cl_read.c1 Sep 2021 14:28:15 -   1.22
+++ cl/cl_read.c1 Sep 2021 17:15:34 -
@@ -62,13 +62,15 

Re: Atomic signal flags for vi(1)

2021-09-01 Thread Ingo Schwarze
Hi Tim,

trondd wrote on Tue, Aug 24, 2021 at 07:45:33PM -0400:
> "Theo de Raadt"  wrote:
 
>> +h_alrm(int signo)
>> +{
>> +   GLOBAL_CLP;
>> +
>> +   F_SET(clp, CL_SIGALRM);
>> 
>> F_SET is |=, which is not atomic.
>> 
>> This is unsafe.  Safe signal handlers need to make single stores to
>> atomic-sized variables, which tend to be int-sized, easier to declare
>> as sig_atomic_t.  Most often these are global scope with the following
>> type:
>> 
>>  volatile sig_atomic_t variable
>> 
>> I can see you copied an existing practice.  Sadly all the other
>> signal handlers are also broken in the same way.
>> 
>> The flag bits should be replaced with seperate sig_atomic_t variables.

> Ok.  Took a swing at converting the signal handling to sig_atomic_t flags.
> No additional changes added other than removing a redundant check of the
> flags in cl_read.c.  Seemed to be a pretty straight-forward conversion and
> I haven't found any change in behavior.

Committed, thank you.
  Ingo


CVSROOT:/cvs
Module name:src
Changes by: schwa...@cvs.openbsd.org2021/09/01 08:28:15

Modified files:
usr.bin/vi/cl  : cl.h cl_funcs.c cl_main.c cl_read.c 

Log message:
As a first step towards safe signal handling, improve the h_int()
and h_winch() signal handlers to make one single store to a
sig_atomic_t variable.  Note that the h_hup() and h_term() signal
handlers are still unsafe after this commit because they also set
the "killersig" (how fitting!) field in a global struct.

Despite storing information in static global variables rather than
in structs passed around as arguments, this patch does not cause a
change in behaviour because there is always exactly one GS object,
initialized using gs_init() called from the top of main(), and
screen_init() stores a pointer to this one and only GS object in
the .gp member of each and every SCR object.  Talk about useless
abstraction...

Problem pointed out by deraadt@.
Patch from Tim  on tech@.
OK deraadt@.


> Index: cl/cl.h
> ===
> RCS file: /cvs/src/usr.bin/vi/cl/cl.h,v
> retrieving revision 1.10
> diff -u -p -r1.10 cl.h
> --- cl/cl.h   27 May 2016 09:18:11 -  1.10
> +++ cl/cl.h   24 Aug 2021 23:34:27 -
> @@ -11,6 +11,11 @@
>   *   @(#)cl.h10.19 (Berkeley) 9/24/96
>   */
>  
> +extern   volatile sig_atomic_t cl_sighup;
> +extern   volatile sig_atomic_t cl_sigint;
> +extern   volatile sig_atomic_t cl_sigterm;
> +extern   volatile sig_atomic_t cl_sigwinch;
> +
>  typedef struct _cl_private {
>   CHAR_T   ibuf[256]; /* Input keys. */
>  
> @@ -45,11 +50,7 @@ typedef struct _cl_private {
>  #define  CL_RENAME_OK0x0004  /* User wants the windows renamed. */
>  #define  CL_SCR_EX_INIT  0x0008  /* Ex screen initialized. */
>  #define  CL_SCR_VI_INIT  0x0010  /* Vi screen initialized. */
> -#define  CL_SIGHUP   0x0020  /* SIGHUP arrived. */
> -#define  CL_SIGINT   0x0040  /* SIGINT arrived. */
> -#define  CL_SIGTERM  0x0080  /* SIGTERM arrived. */
> -#define  CL_SIGWINCH 0x0100  /* SIGWINCH arrived. */
> -#define  CL_STDIN_TTY0x0200  /* Talking to a terminal. */
> +#define  CL_STDIN_TTY0x0020  /* Talking to a terminal. */
>   u_int32_t flags;
>  } CL_PRIVATE;
>  
> Index: cl/cl_funcs.c
> ===
> RCS file: /cvs/src/usr.bin/vi/cl/cl_funcs.c,v
> retrieving revision 1.20
> diff -u -p -r1.20 cl_funcs.c
> --- cl/cl_funcs.c 27 May 2016 09:18:11 -  1.20
> +++ cl/cl_funcs.c 24 Aug 2021 23:34:27 -
> @@ -601,7 +601,7 @@ cl_suspend(SCR *sp, int *allowedp)
>   if (cl_ssize(sp, 1, NULL, NULL, ))
>   return (1);
>   if (changed)
> - F_SET(CLP(sp), CL_SIGWINCH);
> + cl_sigwinch = 1;
>  
>   return (0);
>  }
> Index: cl/cl_main.c
> ===
> RCS file: /cvs/src/usr.bin/vi/cl/cl_main.c,v
> retrieving revision 1.33
> diff -u -p -r1.33 cl_main.c
> --- cl/cl_main.c  5 May 2016 20:36:41 -   1.33
> +++ cl/cl_main.c  24 Aug 2021 23:34:27 -
> @@ -33,6 +33,11 @@
>  
>  GS *__global_list;   /* GLOBAL: List of screens. */
>  
> +volatile sig_atomic_t cl_sighup;
> +volatile sig_atomic_t cl_sigint;
> +volatile sig_atomic_t cl_sigterm;
> +volatile sig_atomic_t cl_sigwinch;
> +
>  static void cl_func_std(GS *);
>  static CL_PRIVATE *cl_init(GS *);
>  static GS  *gs_init(void);
> @@ -217,16 +222,14 @@ h_hup(int signo)
>  {
>   GLOBAL_CLP;
>  
> - F_SET(clp, CL_SIGHUP);
> + cl_sighup = 1;
>   clp->killersig = SIGHUP;
>  }
>  
>  static void
>  h_int(int signo)
>  {
> - GLOBAL_CLP;
> -
> - F_SET(clp, CL_SIGINT);
> + cl_sigint = 1;
>  }
>  
>  static void
> @@ -234,16 +237,14 @@ h_term(int signo)
>  {
>   GLOBAL_CLP;
>  
> - F_SET(clp, 

Re: Atomic signal flags for vi(1)

2021-08-29 Thread Ingo Schwarze
Hi Theo,

Theo de Raadt wrote on Sun, Aug 29, 2021 at 11:38:18AM -0600:
> Ingo Schwarze wrote:

>> *If* more than one GS object ever existed and/or the .gp pointers
>> in different SCR objects could point to different GS objects, this
>> patch might change behaviour.

> If such multiple GS condition ever existed, since signals are (global),
> the handler is only indicating a signal has happened.  And the code
> observing non-zero of these signal-indicating variables would be
> responsible to determine which event it applies to.

Absolutely, that is the sane way to code such stuff.

However, we have all seen signal handlers making complicated decisions
by navigating complex, non-trivial data structures, even in our own
tree.  Of course, those need to be fixed.  But before changing the
code, i still need to figure out whether the seemingly non-trivial
data structure is indeed non-trivial, and which higher-level code
must be made smarter to preserve the functionality, or whether the
apparent complexity merely hides the fact that the data is actually
trivial, as i think it is in this case.

> It is too hard to do this kind of work safely/atomically in a
> signal handler.

100% correct.  And yet, people do try to, and then it even works.
Well, kind of.  Most of the time.  Until it suddenly doesn't.  And
when it suddenly doesn't, people are used to blaming cosmic muons
(and yes, there is indeed about one of those per square centimeter
per minute, on average) rather than thinking something might be
wrong with their code.

Yours,
  Ingo



Re: Atomic signal flags for vi(1)

2021-08-29 Thread Theo de Raadt
> *If* more than one GS object ever existed and/or the .gp pointers
> in different SCR objects could point to different GS objects, this
> patch might change behaviour.

If such multiple GS condition ever existed, since signals are (global),
the handler is only indicating a signal has happened.  And the code
observing non-zero of these signal-indicating variables would be
responsible to determine which event it applies to.  It is too hard
to do this kind of work safely/atomically in a signal handler.



Re: Atomic signal flags for vi(1)

2021-08-29 Thread Ingo Schwarze
Hi,

Theo de Raadt wrote on Sun, Aug 29, 2021 at 02:54:57AM -0600:

> This does look better.
> 
> I appreciate that you are fixing this underlying problem first, before
> overlaying your timer diff.

Indeed.

> Is this working for the vi crowd?

*If* more than one GS object ever existed and/or the .gp pointers
in different SCR objects could point to different GS objects, this
patch might change behaviour.  So the hardest part about veryfying
it is to make sure that only one single GS object exists at any
given time and all .gp pointers in all SCR objects always point to
that one GS object.  GS and [.>]gs are used at hundreds of places,
but i only managed to find a single place where GS is instantiated
(gs_init() in cl/cl_main.c, which is called inly once at the very
beginning of main()), and only a single place where the .gp member
of SCR is ever changed, in screen_init() in common/screen.c, which
is called from a handful of places, but always with a pointer to
that one particular GS object.

So yes, i do think changing members of GS to static globals is safe
and testing with a single screen is sufficient.  After the patch,
 * SIGHUP prints "Hangup" and exits as expected;
 * SIGINT prints "Interrupted" in the status line and exits insert
   mode, but stays in vi(1);
 * SIGTERM prints "Terminated" and exits as expected;
 * window size changes work as expected both in the forground and
   in the background while suspended with Ctrl-Z, and sending
   SIGWINCH manually has no effect, as expected.

Note that the patch is incomplete.  The signal handler functions
h_hup() and h_term() still aren't safe because they still write to
GCLP(__global_list)->killersig.  But this patch performs one
well-defined step and is hard enough to audit already, so let's get
it committed, then take care of killersig in a second step.

So if someone wants to commit, this patch is OK schwarze@.

Alternatively, provide an OK and tell me to commit.

Be careful that the original author of this patch signed as "Tim"
but is not tim@ for all i can tell.

Yours,
  Ingo


> trondd  wrote:
> 
> > "Theo de Raadt"  wrote:
> > 
> > > +h_alrm(int signo)
> > > +{
> > > +   GLOBAL_CLP;
> > > +
> > > +   F_SET(clp, CL_SIGALRM);
> > > 
> > > F_SET is |=, which is not atomic.
> > > 
> > > This is unsafe.  Safe signal handlers need to make single stores to
> > > atomic-sized variables, which tend to be int-sized, easier to declare
> > > as sig_atomic_t.  Most often these are global scope with the following
> > > type:
> > > 
> > >   volatile sig_atomic_t variable
> > > 
> > > I can see you copied an existing practice.  Sadly all the other
> > > signal handlers are also broken in the same way.
> > > 
> > > The flag bits should be replaced with seperate sig_atomic_t variables.
> > 
> > Ok.  Took a swing at converting the signal handling to sig_atomic_t flags.
> > No additional changes added other than removing a redundant check of the
> > flags in cl_read.c.  Seemed to be a pretty straight-forward conversion and
> > I haven't found any change in behavior.
> > 
> > Tim.
> > 
> > 
> > Index: cl/cl.h
> > ===
> > RCS file: /cvs/src/usr.bin/vi/cl/cl.h,v
> > retrieving revision 1.10
> > diff -u -p -r1.10 cl.h
> > --- cl/cl.h 27 May 2016 09:18:11 -  1.10
> > +++ cl/cl.h 24 Aug 2021 23:34:27 -
> > @@ -11,6 +11,11 @@
> >   * @(#)cl.h10.19 (Berkeley) 9/24/96
> >   */
> >  
> > +extern volatile sig_atomic_t cl_sighup;
> > +extern volatile sig_atomic_t cl_sigint;
> > +extern volatile sig_atomic_t cl_sigterm;
> > +extern volatile sig_atomic_t cl_sigwinch;
> > +
> >  typedef struct _cl_private {
> > CHAR_T   ibuf[256]; /* Input keys. */
> >  
> > @@ -45,11 +50,7 @@ typedef struct _cl_private {
> >  #defineCL_RENAME_OK0x0004  /* User wants the windows renamed. */
> >  #defineCL_SCR_EX_INIT  0x0008  /* Ex screen initialized. */
> >  #defineCL_SCR_VI_INIT  0x0010  /* Vi screen initialized. */
> > -#defineCL_SIGHUP   0x0020  /* SIGHUP arrived. */
> > -#defineCL_SIGINT   0x0040  /* SIGINT arrived. */
> > -#defineCL_SIGTERM  0x0080  /* SIGTERM arrived. */
> > -#defineCL_SIGWINCH 0x0100  /* SIGWINCH arrived. */
> > -#defineCL_STDIN_TTY0x0200  /* Talking to a terminal. */
> > +#defineCL_STDIN_TTY0x0020  /* Talking to a terminal. */
> > u_int32_t flags;
> >  } CL_PRIVATE;
> >  
> > Index: cl/cl_funcs.c
> > ===
> > RCS file: /cvs/src/usr.bin/vi/cl/cl_funcs.c,v
> > retrieving revision 1.20
> > diff -u -p -r1.20 cl_funcs.c
> > --- cl/cl_funcs.c   27 May 2016 09:18:11 -  1.20
> > +++ cl/cl_funcs.c   24 Aug 2021 23:34:27 -
> > @@ -601,7 +601,7 @@ cl_suspend(SCR *sp, int *allowedp)
> > if (cl_ssize(sp, 1, NULL, NULL, ))
> > return (1);
> > if (changed)
> > -   F_SET(CLP(sp), CL_SIGWINCH);
> > +  

Re: Atomic signal flags for vi(1)

2021-08-29 Thread Martijn van Duren
I'll see if I can fit this one in in the next few days.
Feel free to remind me :-)

martijn@

On Sun, 2021-08-29 at 02:54 -0600, Theo de Raadt wrote:
> This does look better.
> 
> I appreciate that you are fixing this underlying problem first, before
> overlaying your timer diff.
> 
> Is this working for the vi crowd?
> 
> 
> trondd  wrote:
> 
> > "Theo de Raadt"  wrote:
> > 
> > > +h_alrm(int signo)
> > > +{
> > > +   GLOBAL_CLP;
> > > +
> > > +   F_SET(clp, CL_SIGALRM);
> > > 
> > > F_SET is |=, which is not atomic.
> > > 
> > > This is unsafe.  Safe signal handlers need to make single stores to
> > > atomic-sized variables, which tend to be int-sized, easier to declare
> > > as sig_atomic_t.  Most often these are global scope with the following
> > > type:
> > > 
> > >   volatile sig_atomic_t variable
> > > 
> > > I can see you copied an existing practice.  Sadly all the other
> > > signal handlers are also broken in the same way.
> > > 
> > > The flag bits should be replaced with seperate sig_atomic_t variables.
> > 
> > Ok.  Took a swing at converting the signal handling to sig_atomic_t flags.
> > No additional changes added other than removing a redundant check of the
> > flags in cl_read.c.  Seemed to be a pretty straight-forward conversion and
> > I haven't found any change in behavior.
> > 
> > Tim.
> > 
> > 
> > Index: cl/cl.h
> > ===
> > RCS file: /cvs/src/usr.bin/vi/cl/cl.h,v
> > retrieving revision 1.10
> > diff -u -p -r1.10 cl.h
> > --- cl/cl.h 27 May 2016 09:18:11 -  1.10
> > +++ cl/cl.h 24 Aug 2021 23:34:27 -
> > @@ -11,6 +11,11 @@
> >   * @(#)cl.h10.19 (Berkeley) 9/24/96
> >   */
> >  
> > +extern volatile sig_atomic_t cl_sighup;
> > +extern volatile sig_atomic_t cl_sigint;
> > +extern volatile sig_atomic_t cl_sigterm;
> > +extern volatile sig_atomic_t cl_sigwinch;
> > +
> >  typedef struct _cl_private {
> > CHAR_T   ibuf[256]; /* Input keys. */
> >  
> > @@ -45,11 +50,7 @@ typedef struct _cl_private {
> >  #defineCL_RENAME_OK0x0004  /* User wants the windows renamed. */
> >  #defineCL_SCR_EX_INIT  0x0008  /* Ex screen initialized. */
> >  #defineCL_SCR_VI_INIT  0x0010  /* Vi screen initialized. */
> > -#defineCL_SIGHUP   0x0020  /* SIGHUP arrived. */
> > -#defineCL_SIGINT   0x0040  /* SIGINT arrived. */
> > -#defineCL_SIGTERM  0x0080  /* SIGTERM arrived. */
> > -#defineCL_SIGWINCH 0x0100  /* SIGWINCH arrived. */
> > -#defineCL_STDIN_TTY0x0200  /* Talking to a terminal. */
> > +#defineCL_STDIN_TTY0x0020  /* Talking to a terminal. */
> > u_int32_t flags;
> >  } CL_PRIVATE;
> >  
> > Index: cl/cl_funcs.c
> > ===
> > RCS file: /cvs/src/usr.bin/vi/cl/cl_funcs.c,v
> > retrieving revision 1.20
> > diff -u -p -r1.20 cl_funcs.c
> > --- cl/cl_funcs.c   27 May 2016 09:18:11 -  1.20
> > +++ cl/cl_funcs.c   24 Aug 2021 23:34:27 -
> > @@ -601,7 +601,7 @@ cl_suspend(SCR *sp, int *allowedp)
> > if (cl_ssize(sp, 1, NULL, NULL, ))
> > return (1);
> > if (changed)
> > -   F_SET(CLP(sp), CL_SIGWINCH);
> > +   cl_sigwinch = 1;
> >  
> > return (0);
> >  }
> > Index: cl/cl_main.c
> > ===
> > RCS file: /cvs/src/usr.bin/vi/cl/cl_main.c,v
> > retrieving revision 1.33
> > diff -u -p -r1.33 cl_main.c
> > --- cl/cl_main.c5 May 2016 20:36:41 -   1.33
> > +++ cl/cl_main.c24 Aug 2021 23:34:27 -
> > @@ -33,6 +33,11 @@
> >  
> >  GS *__global_list; /* GLOBAL: List of screens. */
> >  
> > +volatile sig_atomic_t cl_sighup;
> > +volatile sig_atomic_t cl_sigint;
> > +volatile sig_atomic_t cl_sigterm;
> > +volatile sig_atomic_t cl_sigwinch;
> > +
> >  static void   cl_func_std(GS *);
> >  static CL_PRIVATE *cl_init(GS *);
> >  static GS*gs_init(void);
> > @@ -217,16 +222,14 @@ h_hup(int signo)
> >  {
> > GLOBAL_CLP;
> >  
> > -   F_SET(clp, CL_SIGHUP);
> > +   cl_sighup = 1;
> > clp->killersig = SIGHUP;
> >  }
> >  
> >  static void
> >  h_int(int signo)
> >  {
> > -   GLOBAL_CLP;
> > -
> > -   F_SET(clp, CL_SIGINT);
> > +   cl_sigint = 1;
> >  }
> >  
> >  static void
> > @@ -234,16 +237,14 @@ h_term(int signo)
> >  {
> > GLOBAL_CLP;
> >  
> > -   F_SET(clp, CL_SIGTERM);
> > +   cl_sigterm = 1;
> > clp->killersig = SIGTERM;
> >  }
> >  
> >  static void
> >  h_winch(int signo)
> >  {
> > -   GLOBAL_CLP;
> > -
> > -   F_SET(clp, CL_SIGWINCH);
> > +   cl_sigwinch = 1;
> >  }
> >  #undef GLOBAL_CLP
> >  
> > @@ -259,6 +260,11 @@ sig_init(GS *gp, SCR *sp)
> > CL_PRIVATE *clp;
> >  
> > clp = GCLP(gp);
> > +
> > +   cl_sighup = 0;
> > +   cl_sigint = 0;
> > +   cl_sigterm = 0;
> > +   cl_sigwinch = 0;
> >  
> > if (sp == NULL) {
> > if (setsig(SIGHUP, >oact[INDX_HUP], 

Re: Atomic signal flags for vi(1)

2021-08-29 Thread Theo de Raadt
This does look better.

I appreciate that you are fixing this underlying problem first, before
overlaying your timer diff.

Is this working for the vi crowd?


trondd  wrote:

> "Theo de Raadt"  wrote:
> 
> > +h_alrm(int signo)
> > +{
> > +   GLOBAL_CLP;
> > +
> > +   F_SET(clp, CL_SIGALRM);
> > 
> > F_SET is |=, which is not atomic.
> > 
> > This is unsafe.  Safe signal handlers need to make single stores to
> > atomic-sized variables, which tend to be int-sized, easier to declare
> > as sig_atomic_t.  Most often these are global scope with the following
> > type:
> > 
> > volatile sig_atomic_t variable
> > 
> > I can see you copied an existing practice.  Sadly all the other
> > signal handlers are also broken in the same way.
> > 
> > The flag bits should be replaced with seperate sig_atomic_t variables.
> 
> Ok.  Took a swing at converting the signal handling to sig_atomic_t flags.
> No additional changes added other than removing a redundant check of the
> flags in cl_read.c.  Seemed to be a pretty straight-forward conversion and
> I haven't found any change in behavior.
> 
> Tim.
> 
> 
> Index: cl/cl.h
> ===
> RCS file: /cvs/src/usr.bin/vi/cl/cl.h,v
> retrieving revision 1.10
> diff -u -p -r1.10 cl.h
> --- cl/cl.h   27 May 2016 09:18:11 -  1.10
> +++ cl/cl.h   24 Aug 2021 23:34:27 -
> @@ -11,6 +11,11 @@
>   *   @(#)cl.h10.19 (Berkeley) 9/24/96
>   */
>  
> +extern   volatile sig_atomic_t cl_sighup;
> +extern   volatile sig_atomic_t cl_sigint;
> +extern   volatile sig_atomic_t cl_sigterm;
> +extern   volatile sig_atomic_t cl_sigwinch;
> +
>  typedef struct _cl_private {
>   CHAR_T   ibuf[256]; /* Input keys. */
>  
> @@ -45,11 +50,7 @@ typedef struct _cl_private {
>  #define  CL_RENAME_OK0x0004  /* User wants the windows renamed. */
>  #define  CL_SCR_EX_INIT  0x0008  /* Ex screen initialized. */
>  #define  CL_SCR_VI_INIT  0x0010  /* Vi screen initialized. */
> -#define  CL_SIGHUP   0x0020  /* SIGHUP arrived. */
> -#define  CL_SIGINT   0x0040  /* SIGINT arrived. */
> -#define  CL_SIGTERM  0x0080  /* SIGTERM arrived. */
> -#define  CL_SIGWINCH 0x0100  /* SIGWINCH arrived. */
> -#define  CL_STDIN_TTY0x0200  /* Talking to a terminal. */
> +#define  CL_STDIN_TTY0x0020  /* Talking to a terminal. */
>   u_int32_t flags;
>  } CL_PRIVATE;
>  
> Index: cl/cl_funcs.c
> ===
> RCS file: /cvs/src/usr.bin/vi/cl/cl_funcs.c,v
> retrieving revision 1.20
> diff -u -p -r1.20 cl_funcs.c
> --- cl/cl_funcs.c 27 May 2016 09:18:11 -  1.20
> +++ cl/cl_funcs.c 24 Aug 2021 23:34:27 -
> @@ -601,7 +601,7 @@ cl_suspend(SCR *sp, int *allowedp)
>   if (cl_ssize(sp, 1, NULL, NULL, ))
>   return (1);
>   if (changed)
> - F_SET(CLP(sp), CL_SIGWINCH);
> + cl_sigwinch = 1;
>  
>   return (0);
>  }
> Index: cl/cl_main.c
> ===
> RCS file: /cvs/src/usr.bin/vi/cl/cl_main.c,v
> retrieving revision 1.33
> diff -u -p -r1.33 cl_main.c
> --- cl/cl_main.c  5 May 2016 20:36:41 -   1.33
> +++ cl/cl_main.c  24 Aug 2021 23:34:27 -
> @@ -33,6 +33,11 @@
>  
>  GS *__global_list;   /* GLOBAL: List of screens. */
>  
> +volatile sig_atomic_t cl_sighup;
> +volatile sig_atomic_t cl_sigint;
> +volatile sig_atomic_t cl_sigterm;
> +volatile sig_atomic_t cl_sigwinch;
> +
>  static void cl_func_std(GS *);
>  static CL_PRIVATE *cl_init(GS *);
>  static GS  *gs_init(void);
> @@ -217,16 +222,14 @@ h_hup(int signo)
>  {
>   GLOBAL_CLP;
>  
> - F_SET(clp, CL_SIGHUP);
> + cl_sighup = 1;
>   clp->killersig = SIGHUP;
>  }
>  
>  static void
>  h_int(int signo)
>  {
> - GLOBAL_CLP;
> -
> - F_SET(clp, CL_SIGINT);
> + cl_sigint = 1;
>  }
>  
>  static void
> @@ -234,16 +237,14 @@ h_term(int signo)
>  {
>   GLOBAL_CLP;
>  
> - F_SET(clp, CL_SIGTERM);
> + cl_sigterm = 1;
>   clp->killersig = SIGTERM;
>  }
>  
>  static void
>  h_winch(int signo)
>  {
> - GLOBAL_CLP;
> -
> - F_SET(clp, CL_SIGWINCH);
> + cl_sigwinch = 1;
>  }
>  #undef   GLOBAL_CLP
>  
> @@ -259,6 +260,11 @@ sig_init(GS *gp, SCR *sp)
>   CL_PRIVATE *clp;
>  
>   clp = GCLP(gp);
> +
> + cl_sighup = 0;
> + cl_sigint = 0;
> + cl_sigterm = 0;
> + cl_sigwinch = 0;
>  
>   if (sp == NULL) {
>   if (setsig(SIGHUP, >oact[INDX_HUP], h_hup) ||
> Index: cl/cl_read.c
> ===
> RCS file: /cvs/src/usr.bin/vi/cl/cl_read.c,v
> retrieving revision 1.21
> diff -u -p -r1.21 cl_read.c
> --- cl/cl_read.c  27 May 2016 09:18:11 -  1.21
> +++ cl/cl_read.c  24 Aug 2021 23:34:27 -
> @@ -54,34 +54,32 @@ cl_event(SCR *sp, EVENT