Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 29 Sep 1997 15:38:57 +0100
From:      Gareth McCaughan <gjm11@dpmms.cam.ac.uk>
To:        freebsd-bugs@freebsd.org
Subject:   bin/4520 and fmt
Message-ID:  <E0xFgyQ-0006sX-00@g.pet.cam.ac.uk>

next in thread | raw e-mail | index | archive | help
Mikhail Teterin reported a bug in fmt whereby it dumps core when
given a long line (more precisely, it happens when it's given a
line that contains a long "word").

I offer two solutions.

1. The following patch, which also fixes a so-far-unreported bug
   (fmt allows backslashed whitespace inside words, but it only
   counts each such as one character long). (It's relative to the
   version of fmt.c in 2.2-stable as of early August.)

--------------------------- patch begins ---------------------------
*** fmt.c.orig	Mon Sep 29 14:35:34 1997
--- fmt.c	Mon Sep 29 15:04:55 1997
***************
*** 335,347 ****
  	char line[];
  {
  	register char *cp, *cp2;
! 	char word[BUFSIZ];
  	int wordl;		/* LIZ@UOM 6/18/85 */
  
  	cp = line;
  	while (*cp) {
  		cp2 = word;
  		wordl = 0;	/* LIZ@UOM 6/18/85 */
  
  		/*
  		 * Collect a 'word,' allowing it to contain escaped white
--- 335,354 ----
  	char line[];
  {
  	register char *cp, *cp2;
! 	char *word;
  	int wordl;		/* LIZ@UOM 6/18/85 */
+ 	int wordsize, wordleft;
+ 
+ 	word = malloc(BUFSIZ);
+ 	if (word == 0)
+ 		abort();
+ 	wordsize=BUFSIZ;
  
  	cp = line;
  	while (*cp) {
  		cp2 = word;
  		wordl = 0;	/* LIZ@UOM 6/18/85 */
+ 		wordleft = wordsize;
  
  		/*
  		 * Collect a 'word,' allowing it to contain escaped white
***************
*** 349,357 ****
  		 */
  		while (*cp && *cp != ' ') {
  			if (*cp == '\\' && isspace(cp[1]))
! 				*cp2++ = *cp++;
  			*cp2++ = *cp++;
  			wordl++;/* LIZ@UOM 6/18/85 */
  		}
  
  		/*
--- 356,372 ----
  		 */
  		while (*cp && *cp != ' ') {
  			if (*cp == '\\' && isspace(cp[1]))
! 				*cp2++ = *cp++, wordl++, wordleft--;
  			*cp2++ = *cp++;
  			wordl++;/* LIZ@UOM 6/18/85 */
+ 			if (--wordleft < 4) {
+ 				char *oldword = word;
+ 				wordleft += wordsize; wordsize *= 2;
+ 				word = realloc(word, wordsize);
+ 				if (word == 0)
+ 					abort();
+ 				cp2 += word-oldword;
+ 			}
  		}
  
  		/*
***************
*** 363,376 ****
  			if (index(".:!", cp[-1]))
  				*cp2++ = ' ';
  		}
! 		while (*cp == ' ')
  			*cp2++ = *cp++;
  		*cp2 = '\0';
  		/*
  		 * LIZ@UOM 6/18/85 pack(word);
  		 */
  		pack(word, wordl);
  	}
  }
  
  /*
--- 378,401 ----
  			if (index(".:!", cp[-1]))
  				*cp2++ = ' ';
  		}
! 		while (*cp == ' ') {
  			*cp2++ = *cp++;
+ 			if (--wordleft < 3) {	/* yes, 3. Sorry. */
+ 				char *oldword = word;
+ 				wordleft += wordsize; wordsize *= 2;
+ 				word = realloc(word, wordsize);
+ 				if (word == 0)
+ 					abort();
+ 				cp2 += word-oldword;
+ 			}
+ 		}
  		*cp2 = '\0';
  		/*
  		 * LIZ@UOM 6/18/85 pack(word);
  		 */
  		pack(word, wordl);
  	}
+ 	free(word);
  }
  
  /*
***************
*** 382,389 ****
   * there ain't nothing in there yet.  At the bottom of this whole mess,
   * leading tabs are reinserted.
   */
! char	outbuf[BUFSIZ];			/* Sandbagged output line image */
  char	*outp;				/* Pointer in above */
  
  /*
   * Initialize the output section.
--- 407,415 ----
   * there ain't nothing in there yet.  At the bottom of this whole mess,
   * leading tabs are reinserted.
   */
! char	*outbuf;			/* Sandbagged output line image */
  char	*outp;				/* Pointer in above */
+ int	outbuf_size;			/* er, size of outbuf */
  
  /*
   * Initialize the output section.
***************
*** 391,396 ****
--- 417,426 ----
  void
  setout()
  {
+ 	outbuf = malloc(BUFSIZ);
+ 	if (outbuf == 0)
+ 		abort();
+ 	outbuf_size = BUFSIZ;
  	outp = NOSTR;
  }
  
***************
*** 421,426 ****
--- 451,463 ----
  {
  	register char *cp;
  	register int s, t;
+ 
+ 	if (((outp==NOSTR) ? wl : outp-outbuf + wl) >= outbuf_size) {
+ 		outbuf_size *= 2;
+ 		outbuf = realloc(outbuf, outbuf_size);
+ 		if (outbuf == 0)
+ 			abort();
+ 	}
  
  	if (outp == NOSTR)
  		leadin();
---------------------------- patch ends ----------------------------

2. The source for fmt is a real mess. It's full of fixed-size buffers
   and calls to |abort| and basically it's horrible. This will still
   be the case no matter how many fixes of the above type get
   contributed.

   I have a drop-in replacement for it, written by me, which the
   FreeBSD project can have on any reasonable terms. It contains no
   fixed-size buffers, no calls to |abort| and no ghastly ad-hockery.
   It's also significantly faster (not that that matters).

   Official FreeBSD People: Are you interested in this?


A couple of other fmt things:

  - It also appears that my patch for bin/2968 has been applied to fix
    bin/4607 (fine) and that bin/4607 hasn't been closed (not fine).

  - When fmt sees a mailbox From line (`From foo@bar Mon Sep 12 ...')
    it attempts to put it and any following To:, Cc:, Subject: lines
    on output lines of their own.

    It seems to me that this is the Wrong Thing to do. Either mail
    headers should be treated specially all the time (and not just
    those three, of course), even when not preceded by a mailbox
    From line, or they should be treated exactly like ordinary text
    all the time. I vote for the latter.

    The only reason I can see for doing special things with mail
    headers is because people might want to use fmt to format
    mail messages. Mail messages do not generally contain mailbox
    headers; only the mail headers themselves. Does anyone really
    do `fmt /var/mail/me'?

-- 
Gareth McCaughan       Dept. of Pure Mathematics & Mathematical Statistics,
gjm11@dpmms.cam.ac.uk  Cambridge University, England.



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?E0xFgyQ-0006sX-00>