/**** 
 ****   pp  Amiga PagePrint Program
 ****   Version 1.0
 ****/
 
/***
 ***    (C) Copyright 1986  Phil Mercurio (Quicksilver Software)
 ***    All Rights Reserved
 ***
 ***    This software is released as a shareware product.  It may
 ***    be copied providing that no charge is made to the recipient
 ***    and that this copyright notice remains intact.
 ***
 ***    If you use and find value in this software, please send $10
 ***    to the author at this address:
 ***
 ***        Phil Mercurio
 ***        Quicksilver Software
 ***        2515 #9 Camino Del Mar
 ***        Del Mar, CA  92014
 ***
 ***    Be sure to include your name and address, as this will place
 ***    you on the Quicksilver Software mailing list.  Your support
 ***    of shareware products will encourage the further production
 ***    of low-cost software.
 ***/

/**
 ** pp.c
 **
 ** PagePrint program.  Prints text files with a date/time header,
 ** page breaks, and line numbers.  Options include turning off or
 ** varying the line numbers & adding a copyright notice as a footer.
 ** This program is intended to be run from the CLI.
 **
 ** Basic usage:
 **
 **     pp {flags} files
 **
 **
 ** Flags:
 **
 **     -c  Insert copyright notice
 **     -d  Debugging turned on (not useful for the end-user)
 **     -n  Set increment for numbered lines; if followed by 0,
 **         a non-numeric argument, or no argument at all, causes
 **         line numbers to be inhibited.
 **     -q  Suppress messages of current file being printed
 **     -?  Prints Help Message
 **
 ** Flags may appear anywhere on the command line.
 **
 **
 ** Examples:
 **
 **     pp pp.c             || prints this source code file with line numbers
 **                         || every 5th line
 **     pp -qc pp.c pp.h    || prints two source code files, with line 
 **                         || numbers & copyright footer, with no status
 **                         || messages to screen
 **     pp -n 1 pp.c        || prints pp.c with every line numbered
 **     pp -n pp.c          || prints pp.c with no line numbers
 **
 ** Configuring This Program For Your Needs
 **
 ** PP gets most of the information it needs from your printer preferences,
 ** specifically the left and right margin and number of lines.  However,
 ** there are several constants and strings which you may change to 
 ** configure the output to your taste.  All of these constants are grouped
 ** together at the beginning of the header file "pp.h".  By modifying
 ** these constants and recompiling, you can adjust the spacing around
 ** the header and footer, the highlighting used to accentuate the line
 ** numbers, and several other features.  One constant you'll definitely
 ** want to change is "OwnerName", which contains the name to be inserted
 ** in the copyright notice.  The comments in "pp.h" should help.
 **
 **
 ** Quicksilver Software
 ** PJM 860212  Created
 ** PJM 860213  -n & -c working
 ** PJM 860216  -l working
 ** PJM 860307  Removed wildcard expansion (installed "shell" instead)
 ** PJM 860504  Made copyright notice legal
 ** PJM 860808  Removed dependency on "arrange" text formatter;
 **             renamed "-l" as "-n" and added "-d" and "-q".
 ** PJM 860811  Version 1.0 released as a shareware product.
 **/

#include "pp.h" 

main(ac,av)
int ac;
char **av;
{
    register int i;
#ifdef Trace
    printf("main(%d,%lx)\n",ac,av);
#endif Trace

    initialize();
    parseArgs(ac,av);
    configure();

    for(i=0; i < NFiles; i++) {
        if(!Quiet)
            fprintf(stderr,"Printing file \"%s\" ... ",FileName[i]);

        pp(FileName[i]);
	
	if(!Quiet)
	    fprintf(stderr,"done.\n");
	}

    finalize();	
}

/*
 *  Initialize the internal state variables
 */
initialize()
{
#ifdef Trace
    printf("initialize()\n");
#endif Trace

    Enable_Abort = 1;
    
    IntuitionBase = xOpenLibrary("intuition.library",0);

    CopyRight = No;
    Debug = No;
    Quiet = No;

    NFiles = 0;
    LineCount = 5;

    GetPrefs(&Prefs,sizeof(Prefs));
    
    LeftMargin = Prefs.PrintLeftMargin;
    LineWidth = Prefs.PrintRightMargin - Prefs.PrintLeftMargin - Indent;
    LinesPerPage = Prefs.PaperLength - NonBody;
}
 
/*
 *  Parse the argument list.  File names are added to the
 *  FileName array and NFiles is incremented.
 */
parseArgs(ac,av)
int ac;
char **av;
{
    register char *p;
#ifdef Trace
    printf("parseArgs(%d,%lx)\n",ac,av);
#endif Trace
    
    strcpy(MyName,av[0]);
    
    for(ac--, av++; ac > 0; ac--, av++) {
    	if(av[0][0] == '?' && av[0][1] == 0) 
	    helpArgs();
	    
        if(av[0][0] == '-') {
            for(p=&av[0][1]; *p; p++)
                switch(*p) {
		    case '?':
		        helpArgs();
			break;
		    case 'c':  case 'C':
		        CopyRight = Yes;
                        break;
		    case 'd':  case 'D':
		        Debug = Yes;
                        break;
		    case 'n':  case 'N':
		        if(ac > 1 && IsDigit(av[1][0])) {
			    ac--;
			    av++;
			    LineCount = atoi(av[0]);
			    }
                        else
		            LineCount = 0;
			    
			break;
                    case 'q':  case 'Q':
		        Quiet = Yes;
			break;
                    default:
		        fprintf(stderr,"%s: unknown flag '%c'\n",MyName,*p);
			exit(0);
	            }
            }
	else {
	    strcpy(FileName[NFiles],av[0]);
            NFiles++;
	    }
	}
}

/*
 *  Print out a help message and exit.
 */
helpArgs()
{
    int c;
#ifdef Trace
    printf("helpArgs()\n");
#endif Trace


puts("    PagePrint program.  Prints text files with a date/time header,");
puts("    page breaks, and line numbers.  Options include turning off or");
puts("    varying the line numbers & adding a copyright notice as a footer.");
puts("    This program is intended to be run from the CLI.");
puts("");
puts("    Basic usage:");
puts("");
puts("         pp {flags} files");
puts("");
puts("    Flags:");
puts("");
puts("        -c  Insert copyright notice (Note: Change owner name in pp.h)");
puts("        -n  Set increment for numbered lines; if followed by 0,");
puts("            a non-numeric argument, or no argument at all, causes");
puts("            line numbers to be inhibited.");
puts("        -q  Suppress messages of current file being printed");
puts("        -?  This Help Message");
puts("");
puts("     Flags may appear anywhere on the command line.");

    pauseHelp();

puts("");
puts("    Examples:");
puts("");
puts("        pp pp.c             || prints this source code file with line numbers");
puts("                            || every 5th line");
puts("        pp -qc pp.c pp.h    || prints two source code files, with line ");
puts("                            || numbers & copyright footer, with no status");
puts("                            || messages to screen");
puts("        pp -n 1 pp.c        || prints pp.c with every line numbered");
puts("        pp -n pp.c          || prints pp.c with no line numbers");

    pauseHelp();
    
printf("%c0;32;40m",CSI);   /* Fore: 2  Back: 0 */
puts("                                PP (PagePrint)");
puts("            ) Copyright 1986  Phil Mercurio  (Quicksilver Software)");
puts("                              All Rights Reserved");
printf("%c0;31;40m",CSI);   /* Fore: 1  Back:0  (Normal) */

    pauseHelp();
       
puts("    This software is released as a shareware product.  It may");
puts("    be copied providing that no charge is made to the recipient");
puts("    and that this copyright notice remains intact.");
puts("");
puts("    If you use and find value in this software, please send $10");
puts("    to the author at this address:");
puts("");
puts("        Phil Mercurio");
puts("        Quicksilver Software");
puts("        2515 #9 Camino Del Mar");
puts("        Del Mar, CA  92014");
puts("");
puts("    Be sure to include your name and address, as this will place");
puts("    you on the Quicksilver Software mailing list.  Your support");
puts("    of shareware products will encourage the further production");
puts("    of low-cost software.");

    exit(0);
}

/*
 *  Pause the help print out, exit if RETURN not pressed.
 */
pauseHelp()
{
    printf("%c0;33;40m",CSI);   /* Fore: 3  Back: 0 */
    puts("                           Press RETURN to continue\n");
    printf("%c0;31;40m",CSI);   /* Fore: 2  Back:0  (Normal) */
    if(getchar() != 0xA)
        exit(0);
}

/*
 *  Configure global variables based on the parsed arguements.
 */
configure()
{
#ifdef Trace
    printf("configure()\n");
#endif Trace

    if(Debug)
        Prt = stdout;
    else
        Prt = xopen("prt:","a");

    if(Debug) {
        fprintf(stderr,"Debugging ON\n");
	fprintf(stderr,"LeftMargin = %d\n",LeftMargin);
	fprintf(stderr,"LineWidth = %d, LinesPerPage = %d\n",
	    LineWidth, LinesPerPage);
        }

    buildFooter();
}   

/*
 *  Build the copyright footer.
 */
buildFooter()
{
    register int i;
    char buf[StrSiz];
    int y,m,d,wd;
    
    
    for(i=0; i < LineWidth; i++)
        Footer[i] = ' ';
	
    Footer[i] = 0;
    
    DateStamp(&Now);
    parseDate(&Now,&y,&m,&d,&wd);
    sprintf(buf,"(C) Copyright %d",y);
    insStr(Footer,buf);

    insStr(&Footer[(LineWidth - strlen(OwnerName))/2],OwnerName);
    insStr(&Footer[LineWidth - strlen(AllRights)],AllRights);
}

/*
 *  Pretty-print the file file.
 */
pp(file)
char *file;
{
    FILE *fp;
#ifdef Trace
    printf("pp(%s)\n",file);
#endif Trace
    
    getFIB(file, &FIB);
    dateStr(Date,&(FIB.fib_Date));
    timeStr(Time,&(FIB.fib_Date));

    fp = xopen(file,"r");
    
    buildHeader(file,Date,Time);

    ppFile(fp);
    
    fclose(fp);
}

/*
 *  Build the header for the file "file".  The date and time of the file
 *  are used in the header.
 */
buildHeader(file,date,time)
char *file, *date, *time;
{
    register int i;
    static char buf[StrSiz];
    
    for(i=0; i < LineWidth; i++)
        Header[i] = ' ';
	
    Header[i] = 0;
    
    buf[0] = 0;
    strcat(buf,date);
    strcat(buf," ");
    strcat(buf,time);
    
    insStr(Header,buf);
    insStr(&Header[(LineWidth - strlen(file))/2],file);
    insStr(&Header[LineWidth - strlen(PageTemplate)],PageTemplate);
}

/*
 *  Copy the file into Prt from in.  Lines longer than LineWidth chars
 *  are split up, with the continuation lines preceeded by Arrow.
 *  Line numbers are inserted every LineCount lines, if LineCount > 0.
 */
ppFile(in)
FILE *in;
{
    Line = 1;
    Page = 1;
    RealLine = 0;

    beginPage();
	        
    while(fillBuf(in)) {
        writeLine();
        while(emptyBuf())
	    /* null body */ ;
	    
        Line++;
	}

    putLines(LinesPerPage - RealLine);
	
    endPage();
}

/*
 *  Fill the input buffer from the file in.  Returns Yes if successful,
 *  No otherwise.
 */
fillBuf(in)
FILE *in;
{
#ifdef Trace
    printf("fillBuf()\n");
#endif Trace

    if(fgets(Buf1,BufSiz,in) == NULL)
        return(No);
    else {
        expandTabs(Buf1,Buf2);
	BufPtr = Buf2;
	return(Yes);
        }
}

/*
 *  Expand the tabs in in, copying the string to out.  Also removes
 *  the terminating newline and checks for buffer overflow.  Overflowing
 *  lines are truncated.
 */
expandTabs(in,out)
char *in, *out;
{
    register int i;
#ifdef Trace
    printf("expandTabs(%s,%s)\n",in,out);
#endif Trace
    
    i = 0;
    while(!IsEndOfBuffer(*in) && i < BufSiz-1) {
        if(*in == '\t') {
	    in++;
	    do {
	        *out++ = ' ';
		i++;
        	} while(i % TabStop != 0);
	    }
	else {
	    *out++ = *in++;
	    i++;
	    }
	}
	
    *out = 0;
}

/*
 *  Begin a new page by outputting the PreHeader lines, the Header,
 *  and the PreBody lines.
 */
beginPage(fp)
FILE *fp;
{
    register int i;
#ifdef Trace
    printf("beginPage()\n");
#endif Trace

    putLines(PreHeader);        
	
    putSpaces(Indent);

    sprintf(&Header[LineWidth - NPageChars],PageNumFmt,Page);	
    fprintf(Prt,"%s\n",Header);
    
    putLines(PreBody);
}

/*
 *  End a page by outputting the PreFooter and PostFooter lines,
 *  and the Footer, if CopyRight is Yes, else the appropriate number
 *  of blank lines.
 */
endPage()
{
    register int i;
#ifdef Trace 
    printf("endPage()\n");
#endif Trace

    putLines(PreFooter);
	
    if(CopyRight) {
        putSpaces(Indent);
        fprintf(Prt,"%s\n",Footer);
        }
    else {
        putLines(NLFooter);
	}

    putLines(PostFooter);	
}	
	
/*
 *  Write out the first physical line from Buf2, checking for
 *  the need to put out the line number and/or start a new page.
 */
writeLine()
{
    register int i;
    char lineNum[Indent+1];
#ifdef Trace
    printf("writeLine()\n");
#endif Trace

    if(RealLine == LinesPerPage) {
	endPage();
        Page++;
	beginPage();
	RealLine = 0;
        }
	
    if(LineCount != 0 && Line % LineCount == 0) {
        sprintf(lineNum,LineNumFmt,Line);
        putPrefix(lineNum);
	}
    else {
        putSpaces(Indent);
        }

    for(i=0; !IsEndOfBuffer(*BufPtr) && i < LineWidth; i++, BufPtr++)
   	putc(*BufPtr,Prt);

    putLines(1);

    RealLine++;
}

/*
 *  Write out the next LineWidth characters from BufPtr, preceeding it
 *  with an Arrow.  We have to check for the need to page break before
 *  outputting.  No is returned when BufPtr is exhausted, Yes otherwise.
 */
emptyBuf()
{
    register int i;
#ifdef Trace
    printf("emptyBuf()\n");
#endif Trace

    if(IsEndOfBuffer(*BufPtr))
        return(No);
	
    if(RealLine == LinesPerPage) {
	endPage();
        Page++;
	beginPage();
	RealLine = 0;
        }
	
    putPrefix(Arrow);
    for(i=0; i < LineWidth && !IsEndOfBuffer(*BufPtr); i++, BufPtr++)
        putc(*BufPtr,Prt);

    putLines(1);
    RealLine++;
    return(Yes);
}
    
/*
 *  Output the prefix to Prt, using the highlighting commands
 *  as defined in pp.h.  If the highlighting commands are NULL,
 *  they are not output, otherwise they're preceeded by an ESCAPE.
 */
putPrefix(p)
char *p;
{
#ifdef Trace
    printf("putPrefix(%s)\n",p);
#endif Trace

    if(!IsEndOfBuffer(*HiLiteOn))
        fprintf(Prt,"%c%s",ESC,HiLiteOn);
    fprintf(Prt,"%s",p);
    if(!IsEndOfBuffer(*HiLiteOff))
        fprintf(Prt,"%c%s",ESC,HiLiteOff);
}

/*
 *  Put n spaces to Prt.
 */
putSpaces(n)
int n;
{
#ifdef Trace
    printf("putSpaces(%d)\n",n);
#endif Trace

    while(n--)
        putc(' ',Prt);
}

/*
 *  Put n blank lines to Prt.
 */
putLines(n)
int n;
{
#ifdef Trace
    printf("putLines(%d)\n",n);
#endif Trace

    while(n--)
        putc('\n',Prt);
}

/*
 *  Open a file with mode mode, printing an error message if unsuccessful.
 */
FILE *
xopen(file,mode)
char *file, *mode;
{
    FILE *fp;
    
    fp = fopen(file,mode);
    if(fp == NULL) {
        fprintf(stderr,"%s: unable to open file \"%s\"\n",MyName,file);
	exit(0);
	}

    return(fp);
}

/*
 *  Open a library, printing an error message if unsuccessful.
 */
LONG
xOpenLibrary(lib,version)
char *lib;
int version;
{
    LONG libPtr, OpenLibrary();
    
    libPtr = OpenLibrary(lib,0);
    if(libPtr == NULL) {
        fprintf(stderr,"Unable to open library \"%s\"\n",lib);
	exit(0);
	}

    return(libPtr);
}

/*
 *  Print out the argument list.
 */
dbArgs(ac,av)
int ac;
char **av;
{
    register int i;
    
    for(i=0; i < ac; i++)
        printf("%s ",av[i]);
	
    printf("\n");
}

/*
 *  Finalize the program by closing printer.
 */
finalize()
{
#ifdef Trace
    printf("finalize()\n");
#endif Trace

    if(!Debug)
        fclose(Prt);
}

/*
 *  Insert the string from in the string to, but do not terminate
 *  with 0.
 */
insStr(to,from)
char *to, *from;
{
    while(!IsEndOfBuffer(*from))
        *to++ = *from++;
}
