========================================================================
The RCS distributed with 4.2BSD contains at least two idioms of bugs:
incorrect signal catching and failure to test for success of malloc.

RCS proper and rdiff catch signals, even when run in the background.
This class of error is a v6-ism and should no longer happen,
especially since there is a tutorial on the correct way to catch
signals in UNIX Programming by Brian Kernighan and Dennis Ritchie
in Volume 2A of the UNIX Programmer's Manual.
Diffs for rcs/rcsutil.c and rdiff/diffreg.c are at the end of the article.

Nowhere in RCS proper (excluding rdiff and rdiff3) is there a test
that malloc returned a non-NULL pointer.

The diffs are numerous and affect nearly every source file in RCS;
it would be merely tedious to post them.  If you are running RCS
on a PDP-11 or other small machine, you can either find every call
to malloc and change it from

	foo = malloc(bar);

to

	foo = malloc(bar);
	if (foo == NULL)
		faterror("memory exhausted in <function>\n");

or contact me and send a tape (the sources are too big to mail).
In practice, the only program to have run out of memory so far
has been rlog when given an archive with a lot of deltas.

========================================================================
rcutil.c diffs:
______________
70a74,80
> int (*oldsigint)();
> int (*oldsighup)();
> int (*oldsigquit)();
> int (*oldsigpipe)();
> int (*oldsigterm)();
> char sigsread = 0;	/* if true, oldsig* have been set */
> 
188c200,201
< void catchints()
---
> void
> readsigs()
190,192c203,213
< 	signal(SIGINT,catchsig); signal(SIGHUP,catchsig);
< 	signal(SIGQUIT,catchsig); signal(SIGPIPE,catchsig);
< 	signal(SIGTERM,catchsig);
---
> 	oldsigint = signal(SIGINT, SIG_IGN);
> 	signal(SIGINT, oldsigint);
> 	oldsighup = signal(SIGHUP, SIG_IGN);
> 	signal(SIGHUP, oldsighup);
> 	oldsigquit = signal(SIGQUIT, SIG_IGN);
> 	signal(SIGQUIT, oldsigquit);
> 	oldsigpipe = signal(SIGPIPE, SIG_IGN);
> 	signal(SIGPIPE, oldsigpipe);
> 	oldsigterm = signal(SIGTERM, SIG_IGN);
> 	signal(SIGTERM, oldsigterm);
> 	sigsread = 1;
195c216,217
< void ignoreints()
---
> void
> catchints()
197,199c219,242
< 	signal(SIGINT,SIG_IGN); signal(SIGHUP,SIG_IGN);
< 	signal(SIGQUIT,SIG_IGN); signal(SIGPIPE,SIG_IGN);
< 	signal(SIGTERM,SIG_IGN);
---
> 	if (!sigsread)
> 		readsigs();
> 	if (oldsigint != SIG_IGN)	/* don't catch signal if ignoring it */
> 		signal(SIGINT, catchsig);
> 	if (oldsighup != SIG_IGN)
> 		signal(SIGHUP, catchsig);
> 	if (oldsigquit != SIG_IGN)
> 		signal(SIGQUIT, catchsig);
> 	if (oldsigpipe != SIG_IGN)
> 		signal(SIGPIPE, catchsig);
> 	if (oldsigterm != SIG_IGN)
> 		signal(SIGTERM, catchsig);
> }
> 
> void
> ignoreints()
> {
> 	if (!sigsread)
> 		readsigs();
> 	signal(SIGINT, SIG_IGN);
> 	signal(SIGHUP, SIG_IGN);
> 	signal(SIGQUIT, SIG_IGN);
> 	signal(SIGPIPE, SIG_IGN);
> 	signal(SIGTERM, SIG_IGN);
______________
diffreg.c diffs:
______________
163,166c163,170
< 	signal(SIGHUP,done);
< 	signal(SIGINT,done);
< 	signal(SIGPIPE,done);
< 	signal(SIGTERM,done);
---
> 	if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
> 		signal(SIGHUP, done);
> 	if (signal(SIGINT, SIG_IGN) != SIG_IGN)
> 		signal(SIGINT, done);
> 	if (signal(SIGPIPE, SIG_IGN) != SIG_IGN)
> 		signal(SIGPIPE, done);
> 	if (signal(SIGTERM, SIG_IGN) != SIG_IGN)
> 		signal(SIGTERM, done);
______________
========================================================================
The rcs distributed with 4.2 contains the following code fragment
(in src/rdiff/diff.c):

	static	char sccsid[] = "@(#)diff.c 4.1 10/9/80";

	extern	char _sobuf[];

	main(argc, argv)
		int argc;
		char **argv;
	{
		.
		.
		setbuf(stdout, _sobuf);
		.
	}

_sobuf is a buffer declared deep within the stdio library.  Needless
to say this undocumented buffer does not appear in all stdio libraries
as I discovered while porting rcs to Sun Unix 4.2BSD.
(It worked in Sun 4.1c).

Does anyone know what this setbuf is for?  I've just removed it for now.
Walter Tichy are you out there?
========================================================================

When you go to break a lock on a RCS file RCS goes into ultra noisy mode
and sends off mail to the user who had the lock telling them that you broke
the lock. Guess what happens if the user doesn't exist? That's right, it gives
up and says I'm not going to break the lock. Grrrrr. I had to create a alias
for dpk just so I could add stuff to the MDQS RCS archives.

========================================================================

File:  (whatever)/rcs/src/rcs/ci.c
 
Description:  ci allows names following -[nN] to have name delimiter
 	      characters such as white space in them.  This results in
 	      later operations on the file producing errors.
 
Repeat-by:  ci -n"test 1" file
 	    ci -Ntest2 file
 
Fix:	The problem results from the fact the routine checkid()
 	checks a symbol for validity but stops on symbol delimiting
 	characters, the set of which which includes characters other
 	than \0.  ci, however, assumes that the entire name string
 	is a valid symbol if no error occurs, so it installs the
 	entire string as the name.  This results in a situation where a
 	name could be installed consisting of one valid symbol and an
 	arbitrary number of possibly invalid symbols.  The fix is simply
 	to make ci use the information that checkid() returns -- a
 	pointer to the character it stopped on, and to exit with a
 	fatal error (and error message) if the delimiting character was
 	a non-NULL one.
 
RCS file: RCS/ci.c,v
retrieving revision 3.11
retrieving revision 3.9
diff -c -r3.11 -r3.9
*** /tmp/,RCSt1001960	Sat Dec  1 21:39:33 1984
--- /tmp/,RCSt2001960	Sat Dec  1 21:39:42 1984
***************
*** 2,8
   *                     RCS checkin operation
   */
   static char rcsid[]=
!  "$Header: ci.c,v 3.11 84/12/01 21:31:17 chenr Exp $ Purdue CS";
  /*******************************************************************
   *                       check revisions into RCS files
   *******************************************************************

--- 2,8 -----
   *                     RCS checkin operation
   */
   static char rcsid[]=
!  "$Header: ci.c,v 3.9 83/02/15 15:25:44 wft Exp $ Purdue CS";
  /*******************************************************************
   *                       check revisions into RCS files
   *******************************************************************
***************
*** 21,36
  
  
  /* $Log:	ci.c,v $
-  * Revision 3.11  84/12/01  21:31:17  chenr
-  * Changed checkid() test in -[nN] handling section to test the character
-  * pointed to by the pointer and not the pointer itself.
-  * 
-  * Revision 3.10  84/11/30  01:23:01  chenr
-  * Added char *nametest to force name in -[nN]name to be a one word
-  * symbol.  If this isn't the case, then ci aborts with a fatal error.
-  * A quick and dirty fix to prevent names from being placed into the
-  * delta files that won't be properly handled later.
-  * 
   * Revision 3.9  83/02/15  15:25:44  wft
   * original ci.c
   * 

--- 21,26 -----
  
  
  /* $Log:	ci.c,v $
   * Revision 3.9  83/02/15  15:25:44  wft
   * original ci.c
   * 
***************
*** 136,142
  {
          register int i;
          register char * sp, *tp;
-         char *nametest;
          char * cmdusage;         /* holds command format                    */
          char command[NCPPN+50];  /* holds diff commands                     */
          char curdate[datelength];/* date for new delta                      */

--- 126,131 -----
  {
          register int i;
          register char * sp, *tp;
          char * cmdusage;         /* holds command format                    */
          char command[NCPPN+50];  /* holds diff commands                     */
          char curdate[datelength];/* date for new delta                      */
***************
*** 208,217
                          if ((*argv)[2]!='\0'){
                                  if (symbol!=nil)warn("Redefinition of symbolic name");
                                  symbol = (*argv)+2;
! 				  nametest = checkid(symbol,' ');
!                                 if (*nametest != NULL)
! 					faterror("Name %s must be one word",
! 						symbol);
                          } else warn("Missing name for -n option");
                          break;
  

--- 197,203 -----
                          if ((*argv)[2]!='\0'){
                                  if (symbol!=nil)warn("Redefinition of symbolic name");
                                  symbol = (*argv)+2;
!                                 checkid(symbol,' ');
                          } else warn("Missing name for -n option");
                          break;
  
***************
*** 220,229
                          if ((*argv)[2]!='\0'){
                                  if (symbol!=nil)warn("Redefinition of symbolic name");
                                  symbol = (*argv)+2;
! 				  nametest = checkid(symbol,' ');
!                                 if (*nametest != NULL)
! 					faterror("Name %s must be one word",
! 						symbol);
                          } else warn("Missing name for -N option");
                          break;
  

--- 206,212 -----
                          if ((*argv)[2]!='\0'){
                                  if (symbol!=nil)warn("Redefinition of symbolic name");
                                  symbol = (*argv)+2;
!                                 checkid(symbol,' ');
                          } else warn("Missing name for -N option");
                          break;

========================================================================

The sccstorcs converter delivered with 4.2BSD exercises a long-standing
printf bug when used to convert an SCCS archive containing a long delta
commentary.  printf(3S) in the BUGS section says ``Very wide fields
(>128 characters) fail.''

For some inexplicable reason, the author of sccstorcs chose to use
fprintf to write unadorned strings.  He should have used fputs
instead.  Diffs follow (line numbers are approximate, as usual):
276c277
<     fprintf (pd, "%s", description ? description : "\n");
---
>     fputs(description ? description : "\n", pd);
313c314
< 		fprintf (pd, delta -> commentary);
---
> 		fputs(delta -> commentary, pd);

========================================================================

I have recently converted a large number of SCCS files to RCS.
This turned out to be not so easy. Here is one bugfix to sccstorcs.

Unfortunately this fix was not enough. Thre were two more problems:
It is possible to enter the same revision several times into an SCCS-file.
This is used to change the logmessage for a revision. Sccstorcs does not
understand this. It tries to 'ci' the same file several times, with
the result that 'ci' complains: revision number to low.

I solved this with a set of shell and awk procedures. I will not post
these procedures until I'm assured that no one has fixed this problem
with sccstorcs. A fixed sccstorcs is more desirable than my procedures.

There was an additional problem: The SCCS program 'get' bombed on several
of the SCCS files. It turned out that someone had tried to enter
the same file two times, but with different revision numbers.
That 'get' crashed may just be a problem with my porting it to 4.2BSD.
I solved this problem too with the help of some not so beatiful shell
procedures.

Anyway here is a fix to the first problem.

Index: src/new/sccstorcs/src/sccstorcs.c

Bug: When converting from SCCS to RCS the first log message gets lost.

Repeat by: run sccstorcs on an SCCS file, say s.foo.c.
	sccstorcs -t s.foo.c > junk.sccs
	sccstorcs s.foo.c
	rlog foo.c > junk.rcs

	Look at junk.sccs and junk.rcs.
	If junk.rcs lacks the first log message, you got the bug.

Fix:
*** ,,sccstorcs.c	Thu Jun 30 20:26:06 1983
--- sccstorcs.c	Fri Jan 25 22:46:52 1985
***************
*** 25,31
  #define TRUE	1
  #define FALSE	0
  #define SOH	001		/* SCCS lines start with SOH (Control-A) */
! #define RCS	"rcs -q"
  #define GET	"get -s"
  #define CI	"ci -q"
  

--- 25,31 -----
  #define TRUE	1
  #define FALSE	0
  #define SOH	001		/* SCCS lines start with SOH (Control-A) */
! #define RCS     "rcs"
  #define GET	"get -s"
  #define CI	"ci -q"
  
***************
*** 330,335
  char *sccsfile;
  {
      char *rcsfile = &(sname (sccsfile))[2];
  
      if (initialize_rcsfile (header -> description, rcsfile))
  	quit ("Error initializing new rcs file %s\n", rcsfile);

--- 330,338 -----
  char *sccsfile;
  {
      char *rcsfile = &(sname (sccsfile))[2];
+ 
+     if (!(header -> description) && (header -> deltas))
+ 	header -> description = header -> deltas -> commentary;
  
      if (initialize_rcsfile (header -> description, rcsfile))
  	quit ("Error initializing new rcs file %s\n", rcsfile);

========================================================================
