#include "V.h"
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*    box [cols]                                                                *
*                                                                              *
* Draw a box around the input, writing the result to stdout.   The  number  of *
* columns  defaults  to 10.  In any case, if a line longer than this is found, *
* the width is expanded, and the rest of the "box" will be wider.  It is  easy *
* to draw a box around some text from within vi by typing:                     *
*    :-3,+!box                                                                 *
* If  the  right edge isn't straight, you can type a 'u' command and try again *
* with a specific width.                                                       *
*                                                                              *
* There are numerous one-char formatting args that you can use.  Most of  them *
* name  a  "style"  of  box.  A special case is:  a 't' means to trim away any *
* border associated with the style; 'T' will also trim away  any  white  space *
* along either border.  The T/t arg is usually not needed, because most of the *
* "style" args imply trimming, and an upper-case letter  means  to  trim  away *
* white space, too.                                                            *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*
+-----------------------------------------------------+
| There are several styles of boxes.  The default is  |
| like this, which is good for ASCII text, but isn't  |
| too great for code.                                 |
+-----------------------------------------------------+
*/
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* As yet another goodie, this program looks at the first letter of its name to *
* discern the style, if there is no style char on the command line.  The first *
* letter of the name is the style letter, so  you  might  want  to  link  this *
* program to names like "Abox", "Cbox", "sbox", and so on.                     *
*                                                                              *
* The  "box  c",  "box  c",  "cbox"  and  "Cbox"  commands draw boxes around C *
* comments, like the paragraphs you're reading now.  The 'c' style will  strip *
* away  any  '/'  and/or  '*' along the borders; the 'C' style will also strip *
* away white space along both borders.                                         *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -#
# The "script" style is indicated by "box s", "box S", "sbox" or  "Sbox" #
# commands. The text will have '#' for its edges and hyphens for the top #
# and bottom, as this example illustrates. 'S' implies trimming away all #
# white space along the border; 's' just trims away '#' and '-' from the #
# input.                                                                 #
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -#

* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - *
* You can also use '*' or '-' as a style; the  result  will  have  that  char *
* along  the  edges,  and  hyphens  along  the  top  and bottom, as does this *
* paragraph. To get trimming, you need to use the 't' or 'T' args with these. *
* Note that you usually need to say '\*' to protect a '*' from your shell.    *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - *

----------------------------------------------------------------------------
-- There is also an "Ada" style (also useful with other  such  things  as --
-- SNMP MIBs) that has "--" along both edges.  You can get this by saying --
-- "box a", "box A", "abox" or "Abox".  As usual, 'a' means to trim  away --
-- '-' from the input's margins; 'A' means to also trim away white space. --
-- You can also get this style by saying "--" as you might expect.   this --
-- is  a bit of a kludge, but it works (although it doesn't do any of the --
-- trimming that the 'a' and 'A' styles do).                              --
----------------------------------------------------------------------------
*/
/*
      # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #
      # It might be noted that the 'C' style will be adjusted  to  have #
      # an even width; the '*' and '#' styles aren't adjusted, but look #
      # best with an odd width, as you can see with this paragraph.     #
      # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #
*/
char  blankline[] = " ";
int   cols = 10;
char  dblfl = 0;		/* Double edges (as with Ada) */
#define LINSIZ 2000
char  line[LINSIZ];
int   lines = 0;
char  ml=0, mr=0;
char  fl=0, fr=0;
char  style = '+';
char  trimf  = 0;	/* 1: trim away border; 2: also trim white space */
char  trimv[255];	/* True for border chars */
int   wasblank = 0;	/* Counts blank lines skipped */

main(ac,av)
	char**av;
{	int   a, i, n;
	int   c, c0, c1;
	char *p=0;

	ac = Vinit(ac,av);
	av[0] = pname;

	for (a=0; a<ac; a++) {
		V4 "Arg %d: \"%s\"",a,av[a] D;
		switch (c0 = av[a][0]) {
		/*
		* Numeric arguments give the column width.
		*/
		case '7': case '8': case '9':
		case '4': case '5': case '6':
		case '1': case '2': case '3':
		case '0':
			if (sscanf(av[a],"%d",&cols) < 0)
				cols = 10;
			V2 "cols = %d.",cols D;
			break;
		/*
		* Funny chars:
		*/
		case '-':
			if (av[a][1] == '-') {
				V3 "-- style as special case." D;
				style = 'a';
				break;
			}
		case '+':
		case '*':
		case '#':
			style = c0;	/* Any arg specifies the style */
			break;
		case '/':	/* C++ programs */
			style = c0;
			trimf = 1;	/* Always trim away '//' and restore them */
			trimv['/'] = 1;	/* '/' triggers trimming of borders */
			trimv['-'] = 1;	/* Also trim away border hyphens */
			if (av[a][1] == '/') {
				trimf = 2;		/* "//" triggers trimming of spaces. */
			}
			break;
		/*
		* The 't' arg forces trimming of input; 'T' also trims spaces.
		*/
		case 't': trimf = 1; break;
		case 'T': trimf = 2; break;
		/*
		* Upper-case letters also trigger trimming of the input.
		*/
		case 'a':	/* ada programs */
		case 'c':	/* C comments */
		case 's':	/* Scripts */
		case 'A':	/* Ada programs */
		case 'C':	/* C programs */
		case 'S':	/* Scripts */
			style = c0;	/* Any arg specifies the style */
			trimf = 1 + ISupper(c0);
			break;
		case 'b':
		case 'B':
			style = c0;	/* Any arg specifies the style */
			V4 "Default style %d='%c'",style,Dsp(style) D;
			break;
		default:
			V1 "Unknown arg \"%s\" ignored.",av[a] D;
		}
	}
	V4 "cols = %d.",cols D;
	switch (style) {	/* Select margin characters */
	  default:
	case '-':
	case '+': ml = mr = '|'; fl = fr = ' '; break;	/* Text */
	case 'a':
	case 'A': ml = mr = '-'; fl = fr = ' '; dblfl = 1; break;	/* Ada */
	case '/': ml = mr = '/'; fl = fr = ' '; dblfl = 1; break;	/* C++ */
	case 'c':
	case 'C': ml = mr = '*'; fl = fr = ' '; break;	/* C */
	case '*': ml = mr = '*'; fl = fr = ' '; break;	/* C */
	case 's':
	case 'S': ml = mr = '#'; fl = fr = ' '; break;	/* Scripts */
	case '#': ml = mr = '#'; fl = fr = ' '; break;	/* Scripts */
	}
	V6 "style=%d='%c' trimf=%d",style,Dsp(style),trimf D;
	switch (trimf) {
	case 2:
		V3 "Trim spaces." D;
		for (i=0; i<256; i++)
			if (isspace(i))
				trimv[i] = 1;
	case 1:
		V3 "Trim borders." D;
		switch (style) {
			case 'a': case 'A': trimv['-'] = 1;break;
			case 'b': case 'B': trimv['+'] = trimv['-'] = trimv['|'] = 1;break;
			case 'c': case 'C': trimv['/'] = trimv['*'] = 1;break;
			case 's': case 'S': trimv['#'] = trimv['-'] = 1;break;
		}
		break;
	case 0: V3 "No trim." D;
	}
	V5 "Read first line..." D;
	while (Fgets(line,LINSIZ,stdin)) {
		n = strlen(line);
		while (n>0 && (c = line[n-1]) && (c=='\n' || c=='\r')) {
			line[--n] = 0;		/* Trim away CR and LF chars */
		}
		if (trimf) {
			V5 "Trim the %d-byte line ...",n D;
			if ((n = trim(line,n)) < 1) {
				V5 "Trimmed to blank line." D;
				if (lines > 0)
					++wasblank;	/* Count the blank line */
				V5 "Skip blank line %d.",wasblank D;
				continue;		/* Skip it */
			}
		}
		V3 "Got %d-byte line \"%s\"",n,line D;
		while (wasblank-- > 0) {	/* Produce blank lines we skipped */
			V5 "Output blank line %d.",wasblank+1 D;
			outline(blankline,1);
		}
		if (cols < n) {
			cols = n;
			V3 "cols = %d.",cols D;
			switch (style) {	/* Some styles have width restrictions */
			case 'c':
			case 'C':		/* C must be even width to get terminator right */
				if (cols & 1) ++cols;
				break;
			}
		}
		outline(line,n);
		wasblank = 0;
	}
	V5 "End of input." D;
	boxline();
	exit(0);
}
outline(line,n)
	char *line;
	int   n;
{	int   i;

	if (!lines) boxline();	/* Generate top line */
	if (dblfl) putchar(ml);
	if (ml) putchar(ml);
	if (fl) putchar(fl);
	fputs(line,stdout);
	for (i=n; i<cols; i++)
		putchar(' ');
	if (fr) putchar(fr);
	if (mr) putchar(mr);
	if (dblfl) putchar(mr);
	putchar('\n');
	++lines;

}
/* * * * * * * * * * * * * * * *
* Generate top or bottom line. *
* * * * * * * * * * * * * * * */
boxline()
{	int dash, i;

	switch (style) {
	  default:
	case '-': case '+':
		dash = '-';
		putchar('+');
		putchar('-');
		for (i=0; i<=cols; i++)
			putchar(dash);
		putchar('+');
		putchar('\n');
		break;
	case '/':
		dash = '-';
		putchar('/');
		putchar('/');
		for (i=0; i<=cols; i++)
			putchar(dash);
		putchar('-');
		putchar('/');
		putchar('/');
		putchar('\n');
		break;
	case 'a': case 'A':
		dash = '-';
		putchar('-');
		putchar('-');
		putchar('-');
		for (i=0; i<=cols; i++)
			putchar(dash);
		putchar('-');
		putchar('-');
		putchar('\n');
		break;
	case 'c': case 'C':
		dash = '*';
		if (!lines) putchar('/');	/* Top line */
		putchar('*');
		if (cols&1 == 1) ++cols;	/* Must be even width */
		for (i=0; i<=cols+1; i++)
			putchar(i&1 ? dash : ' ');
		if (lines) putchar('/');	/* Bottom line */
		putchar('\n');
		break;
	case '*': case '#':
	case 's': case 'S':
		dash = '-';
		putchar(mr);
		putchar(' ');
	/*	if (cols&1 == 1) ++cols;	/* Must be even width */
		for (i=0; i<=cols; i++)
			putchar(i&1 ? ' ' : dash);
		putchar(mr);
		putchar('\n');
		break;
	}
}
/*
* Trim away border junk.  This depends on what kind of border
* we are drawing, of course.
*/
trim(p,r)
	char*p;
	int  r;		/* Right edge (+1) */
{  int  l=0;	/* Left edge */
	int  i;
	char c;
	Fenter("trim");
	V6 "r=%d \"%s\"",r,p D;
	while ((l<r) && (c=p[l]) && trimv[c]) {
		++l;		/* Trim left edge */
		V5 "Trim left c=%02X='%c' \"%s\"",c,c,p+l D;
	}
	p[r] = 0;
	V5 "l=%d \"%s\"",l,p+l D;
	while ((l<r) && (c=p[r-1]) && trimv[c]) {
		V5 "Trim right c=%02X='%c'",c,c D;
		p[--r] = 0;	/* Trim right edge */
	}
	V5 "r=%d \"%s\"",r,p+l D;
	if (l > 0) {
		for (i=l; i<r; i++) {
			p[i-l] = p[i];	/* Shift left by l bytes */
		}
		r -= l;
	}
	p[r] = 0;
	V5 "r=%d \"%s\"",r,p D;
	Fexit;
	return r;
}
