/* copyright (c) 1982 Peter Baker */

/*
   Wrap files to the standard output with any of a number of handy
   options.  This program grew all out of proportion with people
   continually asking for an extra feature.  Please don't curse me
   for the poor code ... I was only trying to do them a favour.  Do
   not try to add any more features without restructuring the whole
   thing or it is liable to explode!!!
*/
#include <stdio.h>

#define NEWLINE		'\n'
#define CR			'\r'
#define TAB			'\t'
#define BKSPC		'\b'
#define FORMFEED	'\f'
#define SPACE		' '


#ifndef true
#define true 1
#define false 0
#endif

/*
 * See 'wrap.doc' for info and options.
 */

int BIGBUF = 0x1000;			/* 4K ... could be up to ~ 40K */
int width = 80;
int length = 60;			/* standard 8 1/2 by 11 inch paper */
int headings = false;
int tabs = false;
int tabsize;
int slinenum = 0;			/* start printing at line N */
int indent;
int date = false;
char *filename;
FILE filep;
char *datecode;
int colcnt;
int linecnt;
int pagecnt;

main( argc, argv )
int argc;
char *argv[];
{
	static int fnum = 1;	/* arg # of the first filename if no opt */
	static int optnum = 1;

	if( --argc < 1 )
		usage();	/* this exits !! */

	while( *argv[optnum] == '-' )	/* options ?? */
	{
		if( --argc < 1 )	/* must be one arg left */
			usage();	/* usage() exits! */
		fnum++;	/* since this arg is an option */
		switch( *(argv[optnum] +1) )
		{
			case 'w':
			case 'W': width = atoi( argv[optnum] + 2 );
					  /* skip '-w' */
					  break;
			case 'p':
			case 'P': length = atoi( argv[optnum] + 2 );
					  break;
			case 'h':
			case 'H': headings = true;
					  length -= 3;	/* to acct for hdg space */
					  break;
			case 't':
			case 'T': tabs = true;
					  tabsize = atoi( argv[optnum] + 2 );
					  if( tabsize == 0 )
						  tabsize = 8;	/* default */
					  break;
			case 'd':
			case 'D': date = true;
					  headings = true;
					  datecode = argv[optnum] + 2;
					  break;
			case 'l':
			case 'L': slinenum = atoi( argv[optnum] + 2 );
					  break;
			case 'i':
			case 'I': indent = atoi( argv[optnum] + 2 );
					  break;
			case 'b':
			case 'B': BIGBUF = atoi( argv[optnum] + 2 );
					  break;
			default:  usage();
		}
		optnum++;	/* process next option */
	}
#ifdef DEBUG
printf( "argv[1] = %s\n", argv[1] );
printf( "\nwidth = %d\n", width );
printf( "argv[2] = %s\n", argv[2] );
printf( "length = %d\n", length );
#endif
	while( argc-- )
	{
		filename = argv[fnum];
		filep = fopen( filename, "r" );
		if( filep == 0 )
			printf( "\nwrap cannot open: %s\n", filename );
		else
		{
			wrap();
			fclose( filep );
			if( length > 0 )
				putchar( FORMFEED );
			putchar( NEWLINE );
		}
		fnum++;
	}
}

wrap()
{
	static int c;		/* this is an int so it can hold an EOF */
	static int spcs, i, lntemp;

	fillbuf();			/* this is needed here for some unknown reason */
	linecnt = 0;
	colcnt = indent;
	pagecnt = 1;
	if( headings )
		prhdg( pagecnt++ );

	if( slinenum )		/* wait for the slinenum'th line before we print */
		for( lntemp=0 ; lntemp < slinenum ; lntemp++ )
			while( ( ( c = bgetc() ) != '\n' ) && ( c != EOF ) )
				;
	doindent();
	while( ( c = bgetc() ) != EOF )
	{
		switch( c )
		{
			case NEWLINE:	colcnt = indent;
							linecnt++;
							break;
			case CR:		colcnt = indent;
							break;
			case TAB:		spcs = tabsize - ( (colcnt-indent) % tabsize );
							/* this starts tabs at the indent column not 0 */
							if( spcs == 0 )
								spcs = tabsize;
							colcnt += spcs;
							if( tabs )
								for( i=0 ; i<spcs ; i++ )
									putchar( SPACE );
#ifdef DEBUG
printf( "\n spcs = %d\n", spcs );
#endif
							break;
			case BKSPC:		colcnt -= 1;
							break;
			case FORMFEED:	linecnt = 0;
							colcnt = indent;
							break;
			default :		colcnt++;
		}
		if( colcnt >= width )
		{
			putchar( NEWLINE );
			doindent();
			colcnt = indent;
			linecnt++;
		}

		if( tabs && ( c == TAB ) )
			;	/* don't print the tab if already done */
		else
			putchar( c );

		if( c == '\n' )
			doindent();

		if( headings && ( c == FORMFEED ) )
		{
			prhdg( pagecnt++ );
			doindent();
		}
		else if( c == FORMFEED )
			doindent();

		if( length > 0 )
			if( linecnt >= length )
			{
				putchar( FORMFEED );
				if( headings )
					prhdg( pagecnt++ );
				putchar( CR );		/* cursor at col 0 */
				doindent();
				linecnt = 0;
				colcnt = indent;
			}
	}
}


prhdg( pagenum )
int pagenum;
{
	putchar( NEWLINE );
	doindent();
	printf( "       File:    %s", filename );
	printf( "       Page:  %d", pagenum );
	if( date )
		printf( "       Date:    %s", datecode );
	putchar( NEWLINE );
	putchar( NEWLINE );
}


usage()
{
/* The msg is broken up due to C/80 limiting line length to 100 chars. */

static char *msg = "[-h] [-ttabsize] [-dstring] fname [fname fname ...]";

	printf( "\nUsage: wrap [-wpagewidth] [-ppagelength] %s\n", msg );
	printf( "where:\t-wN  sets the pagewidth to N\n" );
	printf( "\t-pN  sets the pagelength to N\n" );
	printf( "\t-h   turns on headings at the top of each page\n" );
	printf( "\t-tN  expands tabs to every Nth column\n" );
	printf( "\t-dstring sets the date to string and turns on headings\n" );
	printf( "\t-lN  starts printing at the Nth line of the file\n" );
	printf( "\t-iN  indents each line N spaces\n" );
	printf( "\t-bN  sets the file buffer to N bytes\n" );
	exit(1);
}

static unsigned numchars = BIGBUF;
/* Number of chars in buffer.  Set to BIGBUF to get bgetc() started. */

static int remchars = 0;	/* # of available chars remaining in buffer */
static char *begbuf;		/* pointer to beginning of buffer */
static char *curbyte;		/* pointer to next available byte in buffer */
static char kickit = true;	/* initialize flag */

bgetc()
{
	if( remchars-- > 0 )
		return( *curbyte++ );
	else
	{
		if( numchars < BIGBUF )
		{
			numchars = BIGBUF;		/* reset for next file */
			return( EOF );
		}
		else
		{
			fillbuf();
			return( bgetc() );
		}
	}
}

fillbuf()
{
	static unsigned int i;

	if( kickit )
	{
		if( ( begbuf = sbrk( BIGBUF ) ) == -1 )
		{
			printf( "\nError: cannot allocate buffer.\n" );
			printf( "Try smaller buffer size.\n" );
			exit(1);
		}
		kickit = false;
	}
	i = 1;
	curbyte = begbuf;

/* read until either EOF or buffer is full */
	while( ( *curbyte++ = getc( filep ) ) != EOF )
		if( i == BIGBUF )
			break;
		else
			i++;

	remchars = numchars = i;
	curbyte = begbuf;
}

doindent()
{
	int i;
	for( i=0 ; i<indent ; i++ )
		putchar( SPACE );
}
# of available chars remaining in buffer */
static char *begbuf;