From: David Ruggiero <osiris@halcyon.com>
Subject: SUMMARY: What do *you* use to format mail text?
Followup-To: comp.unix.questions
Originator: osiris@halcyon.com
Keywords: mail format script
Reply-To: osiris@halcyon.halcyon.com (David Ruggiero)
Organization: [none - why fight entropy?]
Date: Mon, 21 Dec 1992 17:03:13 GMT


I had asked the question (in several newsgroups) about scripts people
use to format their incoming/outgoing mail text, including evening out
line lengths and adding indent characters. Several people responded: a
few were quite unhelpful, suggesting I use emacs (sorry), long series
of arcane vi commands (that's what I'm trying to get away from!), or
fmt(1) (despite the clear statement in my post that fmt couldn't do
what I needed).

Many, however, were very knowledgable and provided scripts of various
degrees of capability and complexity to get the job done. Thanks to
all five of you recognized below...using these routines, I find I
can now waste my time on the net much more efficiently. :)

David Ruggiero (osiris@halcyon.com)

------insert_#1-----------------------------------------------------------
{I'll actually start by completely countering what I said above and give
this nifty vi command string to format one line at a time. Simple and
effective and fast.   - jdr}


  From: galway@chtm.eece.unm.edu (Denis McKeon)
  Newsgroups: comp.unix.questions
  
  While '!fmt' does a nice job for large blocks, I find I prefer to work
  a line at a time (possibly repeated for paragraphs):
  
      map + J074lBhr
  
  This joins lines and then splits them between words left of column 74. 
  Inter-sentence spacing is unaffected.  The solution doesn't depend on
  anything external to 'vi' - you could use it within a 'vi' clone under
  DOS, for instance.  There is no fork overhead (which was a bigger
  problem when I was running Unix on an 8088 in a DEC Rainbow). Since
  David said he was using vi, he could add this to his .exrc file or
  EXINIT environment variable.  Your mileage may vary.
 
------insert_#2---------------------------------------------------------- 
{Now a short but effective script that uses nroff to do the dirty work.
This is slower, but it works and can be customized to your particular
needs. This is a great lesson in using existing tools rather that writing
your own!      - jdr}


  From: Ray A. Jones <ray@camco1.celestial.com>
  
  Among other "map" items in my .exrc file, I have
  
  map OS mz}bmx:'z,'x!xfmt
       ^^^ 
        |  Function key F4
  When I want to reformat a paragraph, I put the cursor on the first character
  of the paragraph and press F4.  If you want to use it that way, replace OS
  with whatever key or function key you want.
  
  {This is the "xfmt" script, referenced above:}
  
  echo "" >/tmp/.xy
  echo ".na
  .nh
  .ll 6.5i" >/tmp/xx
  cat - >>/tmp/xx
  nroff /tmp/xx|sed -e "/^$/d" |cat - /tmp/.xy
  rm /tmp/xx /tmp/.xy
  
------insert_#3-----------------------------------------------------------
{A good suggestion for a good reference.    - jdr}

  From: kkress@phent.UUCP (Kenneth R. Kress)
  Subject: Mail formatting scripts
  
   In "Sed & Awk" by Dale Dougherty (O'Reilly & Associates, Inc.)
   such a script is described. It uses nawk (which I don't have) and is
   too long for me to want to type in. If you can get your hands on the
   book, look at "adj" in the Miscellany of Scripts chapter (in my edition,
   p. 329-336).

-----insert_#4------------------------------------------------------------
{A highly customizable perl script ('reform') that works well.    - jdr}

  From: lwall@netlabs.com <Larry Wall>
  
  #!/usr/bin/perl
  # Usage: reform [-lNUM] [-rNUM] [-iNUM] [-sNUM] [files]

  # Set default values for left margin, right margin, indent
  # and paragraph spacing.
  $l = 0;
  $r = 0;
  $i = 0;
  $s = 1;
  
  # Process any switches.
  while ($ARGV[0] =~ /^-/) {
      $_ = shift;
      /^-(l|r|i|s)(\d+)/ && (eval "\$$1 = \$2", next);
      die "Unrecognized switch: $_\n";
  }
  
  # Calculate format strings.
  $r = $l + 65 unless $r;
  $r -= $l;                       # make $r relative to $l
  die "Margins too close\n" if $l + $i >= $r;
  
  $LEFT = ' ' x $l;
  $INDENT = ' ' x $i;
  $RIGHT1 = '^' . '<' x ($r - 1 - $i);
  $RIGHT2 = '^' . '<' x ($r - 1);
  $SPACING = "\n" x $s;
  
  # Define a format at run time.
  $form = <<"End of Format Definition";
  format STDOUT =
  $LEFT$INDENT$RIGHT1
  \$_
  $LEFT$RIGHT2~~
  \$_
  $SPACING.
  End of Format Definition
  
  print $form if $debugging;
  eval $form;
  
  # Set paragraph mode on input.
  $/ = '';
  
  # For each paragraph...
  while (<>) {
      s/\s+/ /g;                      # Canonicalize white space.
      s/ $//;                         # Trim final space.
      s/([Ia-z0-9][)'"]*[.!?][)'"]*) /$1  /g; # Fix sentence ends.
      write;                          # Spit out new paragraph.
  }
-----insert_#5 (long)--------------------------------------------------------
{Finally, a full-blown C program. Enjoy!    - jdr}

From: amos shapir <amos@CS.HUJI.AC.IL>
Here is the whole package, man pages included:

--Cut-here-------X---------------X---------------X---------------X----
#!/bin/sh
# This is a shell archive (shar 3.32)
# made 12/20/1992 06:54 UTC by amos@hal
# Source directory /a/falafel/home/phd/amos/misc/adj
#
# existing files WILL be overwritten
#
# This shar contains:
# length  mode       name
# ------ ---------- ------------------------------------------
#    685 -rw-r--r-- adj.txt
#   1164 -rw-r--r-- adj.6
#   3395 -rw-r--r-- adj.c
#    316 -rw-r--r-- ctr.6
#    271 -rw-r--r-- ctr.c
#
if touch 2>&1 | fgrep 'amc' > /dev/null
 then TOUCH=touch
 else TOUCH=true
fi
# ============= adj.txt ==============
echo "x - extracting adj.txt (Text)"
sed 's/^X//' << 'SHAR_EOF' > adj.txt &&
X                    Have    you    ever
X                 wished  you had  something
X              to  replace  nroff?   Something
X            small, fast  and versatile,  yet at
X           the same time  flexible and powerful?
X          Well, your troubles are over.  'Adj' is
X          not  as powerful  as nroff  or TeX,  but
X          it's ok for small portions of text, (and
X          much  more  useful than  4BSD's  'fmt').
X          Combined  with  the  trivial  centering
X           filter 'ctr',  it can do  fancy stuff
X            like  this  text.   This  text  was
X                  produced by the command:
X
Xadj -w 19,26,31,35,37,39,40,40,40,39,37,35,31,26,19 | ctr -w 60
SHAR_EOF
$TOUCH -am 1025141788 adj.txt &&
chmod 0644 adj.txt ||
echo "restore of adj.txt failed"
set `wc -c adj.txt`;Wc_c=$1
if test "$Wc_c" != "685"; then
	echo original size 685, current size $Wc_c
fi
# ============= adj.6 ==============
echo "x - extracting adj.6 (Text)"
sed 's/^X//' << 'SHAR_EOF' > adj.6 &&
X.TH ADJ 6 Local
X.UC 4
X.SH NAME
Xadj \- fill and adjust text lines
X.SH SYNOPSIS
X.B adj
X[
X.B \-h
X] [
X.B \-n
X] [
X.B \-p
X] [
X.B \-w
X.I n1,...
X] [ file ]
X.SH DESCRIPTION
X.I Adj
Xcopies the given file (standard input if none is given) to the standard output,
Xfilling lines and adjusting the right margin. Indentation and empty lines
Xare preserved.
X.PP
XNormally, words (uninterrupted sequences of non-blank characters) are not
Xbroken, unless the
X.B \-h
X(hyphen) flag is given.
X.PP
XThe
X.B \-n
X(no adjust) prevents
X.I adj
Xfrom adjusting the right margin.
X.PP
XIf the 
X.B \-p
X(paragraph) option is specified, indentation is preserved only on
Xthe first line of a paragraph, assuming paragraphs are separated by empty lines.
X.PP
XThe
X.B \-w
X.I n
X(width) option sets the line width to
X.I n
X(default 65).
XWhen given as
X.B \-w
X.I n1,n2,...
X(a comma-separated sequence of numbers) the output lines will be
X.I "n1, n2"
Xetc. characters long, respectively.
XWhen the sequence is exhausted, it is restarted.
X.SH "SEE ALSO"
Xctr(6)
X.SH BUGS
X.B \-n
Xdoes not break words intelligently.
X.PP
XChange of indentation is done ungracefully.
X.PP
XThere's probably a better way tp specify line lengths.
SHAR_EOF
$TOUCH -am 0319114087 adj.6 &&
chmod 0644 adj.6 ||
echo "restore of adj.6 failed"
set `wc -c adj.6`;Wc_c=$1
if test "$Wc_c" != "1164"; then
	echo original size 1164, current size $Wc_c
fi
# ============= adj.c ==============
echo "x - extracting adj.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > adj.c &&
X/*
X * Trivial line-fill filter
X * white space at col 1 and empty lines
X * are preserved.
X * flags: -n - dont adjust right margin
X *	  -w {n} line is {n} chars wide
X *	  -w {n1,n2,...} line is cyclically n1, n2... chars wide
X *	  -h - break words with a hyphen
X *	  -p - indent only 1st line of a paragraph
X */
X#define MAXB 1024
X#define MAXW 100	/* words in a line */
X#define NLL  50		/* line lengths vector size */
X#define EOF  (-1)
X/* flags */
X#define NOADJ 01	/* no adjust */
X#define HYPHN 02	/* hyphenate */
X#define PARAG 04	/* indent 1 line */
X#define UNBUF 010	/* unbuffered */
Xint ll[NLL] = { 65 };	/* line lengths */
Xint nll;		/* current line length index */
Xint nw;		/* current no. of words */
Xint ind;	/* current indentation */
Xstatic char buf[MAXB];
Xmain(argc, argv)
X	char **argv;
X{
X	register char *pb, *pw, *pe;
X	register c, flags, nl;
X
X	flags = 0;
X	while(--argc > 0)
X		if(**++argv == '-')
X			switch(argv[0][1]) {
X			case 'n':
X				flags = NOADJ;
X				break;
X			case 'h':
X				flags = HYPHN;
X				break;
X			case 'p':
X				flags |= PARAG;
X				break;
X			case 'w':
X				--argc;
X				pb = *++argv;
X				nl=0;
X				do {
X					for(ll[nl]=0; *pb>='0' && *pb<='9'; pb++)
X						ll[nl] = ll[nl]*10+*pb-'0';
X					nl++;
X				}while(*pb++);
X				break;
X			}
X		else {
X			close(0);
X			open(*argv, 0, 0);
X		}
X/*
X * When processing, buf looks like this:
X * buf                 pw pb      pe
X * v----+-+----+-+----+v+-v-------v+
X * |word| |word| |word| |wo        |
X * +----+-+----+-+----+-+----------+
X * nw counts full words, nl empty lines.
X */
X	nl = 2;
X	pb = pw = buf-1;
X	while((c = getchar()) != EOF) {
X		if(c==' ' || c=='\t' || c=='\n') {
X			if(pb>=buf && *pb!=' ') {
X				/* end of a sentence */
X				if(!(flags&HYPHN) &&
X				  (*pb=='.' || *pb=='!' || *pb=='?'))
X					*++pb = '\037';
X				*++pb = ' ';
X				nw++;
X				pw = pb;	/* word marker in buf */
X			}
X			if(c == '\n') {
X				if(++nl > 1) {	/* end of paragraph */
X					if(nw > 0) {
X						putline(pb, NOADJ);
X						pb = pw = buf-1;
X						nw = 0;
X					}
X					putchar('\n');
X					ind = 0;
X				}
X			} else if(nl > 1) {
X				if(c == '\t')
X					ind = (ind/8+1)*8;
X				else
X					ind++;
X			}
X		} else if(c>' ' && c<0177) {
X			*++pb = c;
X			if(nl) {
X				nl = 0;
X				pe = &buf[ll[nll]-ind];
X			}
X		}
X		if(pb >= pe) {	/* line overflow */
X			if(*pe == '\037') {	/* that's the only case pb>pe */
X				*pe = *pb--;
X				pw = pe;
X			}
X			if((flags&HYPHN) && pb-pw>2) {	/* insert hyphen */
X				*++pb = pe[-1];
X				pe[-1] = '-';
X				*++pb = c;
X				pw = pe;
X				nw++;
X			} else if(nw == 0) {	/* insert blank */
X				*++pb = c;
X				pw = pe;
X				nw++;
X			}
X			putline(pw, flags);
X			for(pe=buf, ++pw; pw<=pb; )
X				*pe++ = *pw++;
X			pb = pe-1;
X			pw = buf-1;
X			pe = &buf[ll[nll]-ind];
X			nw = 0;
X		}
X	}
X	if(nw > 0)
X		putline(pb, NOADJ);
X	return(0);
X}
X
X/*
X * adjust & print line
X */
Xputline(pw, flags)
X	register char *pw;
X{
X	register char *pb;
X	register i, n, b, m;
X
X	for(i=ind; i>=8; i-=8)
X		putchar('\t');
X	for(; i>0; i--)
X		putchar(' ');
X	n = nw-1;
X	if(pw[-1] == '\037')	/* end of sentence at end of line */
X		--pw;
X	b = buf+ll[nll]-ind-pw;	/* no. of blanks to distribute */
X	m = n/2;
X	for(pb=buf; pb<pw; pb++) {
X		putchar(*pb == '\037' ? ' ' : *pb);
X		if(!(flags&NOADJ) && *pb==' ') {
X			m += b;
X			for(i=m/n; i>0; i--)
X				putchar(' ');
X			m %= n;
X		}
X	}
X	putchar('\n');
X	/* cyclic increment line lengths vector */
X	if(ll[++nll] == 0)
X		nll = 0;
X	if(flags&PARAG)
X		ind = 0;
X}
SHAR_EOF
$TOUCH -am 0527174191 adj.c &&
chmod 0644 adj.c ||
echo "restore of adj.c failed"
set `wc -c adj.c`;Wc_c=$1
if test "$Wc_c" != "3395"; then
	echo original size 3395, current size $Wc_c
fi
# ============= ctr.6 ==============
echo "x - extracting ctr.6 (Text)"
sed 's/^X//' << 'SHAR_EOF' > ctr.6 &&
X.TH CTR 6 Local
X.UC 4
X.SH NAME
Xctr \- center text lines
X.SH SYNOPSIS
X.B ctr
X[
X.B \-w
X.I n
X]
X.SH DESCRIPTION
X.I Ctr
Xcopies its standard input to its standard output,
Xcentering text in the output lines.
X.PP
XThe
X.B \-w
X.I n
X(width) option sets the output line width to
X.I n
X(default 65).
X.SH "SEE ALSO"
Xadj(6)
X.SH BUGS
SHAR_EOF
$TOUCH -am 0318144287 ctr.6 &&
chmod 0644 ctr.6 ||
echo "restore of ctr.6 failed"
set `wc -c ctr.6`;Wc_c=$1
if test "$Wc_c" != "316"; then
	echo original size 316, current size $Wc_c
fi
# ============= ctr.c ==============
echo "x - extracting ctr.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > ctr.c &&
X#define NULL ((char *)0)
Xmain(argc, argv)
X	char **argv;
X{
X	static int ll = 65;	/* line length */
X	static buf[100];
X
X	if(argc > 1 && **++argv == '-' && argv[0][1] == 'w')
X			ll = atoi(*++argv);
X	while(gets(buf) != NULL)
X		printf("%*s%s\n", (ll-strlen(buf))/2, "", buf);
X}
SHAR_EOF
$TOUCH -am 0318150987 ctr.c &&
chmod 0644 ctr.c ||
echo "restore of ctr.c failed"
set `wc -c ctr.c`;Wc_c=$1
if test "$Wc_c" != "271"; then
	echo original size 271, current size $Wc_c
fi
exit 0

--Cut-here-------X---------------X---------------X---------------X----

-----end of all inserts----

Hope these help others...again, thanks to those who took the time and trouble
to respond.


-- 
David Ruggiero  (jdavid@halcyon.com)       Seattle, WA: Home of the Moss People
Newsgroups: comp.unix.questions