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>
