/*
 *   ------------------------------------
 *   C R A M   -   the ASCII file reducer
 *   ------------------------------------
 *
 *   CRAM  -  ASCII File Reducer  - V3.3
 *   Copyright (c) 1988-93  -  Dean Tutterow  -  All Rights Reserved
 *   
 *   What  does  it do?   It crams as  much text  as possible onto a page
 *   in  reduced format.   Using  subscript characters as  the font on an
 *   Epson printer,  you  can print up to 79 characters wide and 154 rows
 *   long in 2 columns.   That  works  out to  5-6 pages of  text on each
 *   printed  page.   In  normal use  with  files with embedded formfeeds
 *   respected, you get 4 pages of text on each printed page.
 *
 *   CRAM also supports the HPLJ  and related printers.  Prior to release
 *   V2.2, the file CRAM.DAT  would have to be  used to send the required
 *   initialization and termination strings.  Version 2.2 has implemented
 *   embedded HPLJ strings.  The current  functionality is to select font
 *   number  1006  (6  point  font)  and  compute  the  appropriate  line
 *   separation.  At  termination, font  number 1012  (12 point  font) is
 *   selected and the line  separation is set to  6 lines per inch.  This
 *   implementation is only an example of  what YOU can do.  I personally
 *   do not use the  standard LJ fonts.  I  load three soft fonts, 1006 -
 *   the 6 pt. font,  1012 - a 12  pt. default font, and  1024 - a 24 pt.
 *   font.   If  you have  a  LJ  printer, change  the  initialization of
 *   GLOBAL  symbol HPLJ  from 0 to  1.   Add in  YOUR initialization and
 *   termination  strings  and  recompile!
 *
 *   CRAM  was  written  after  I  had  printed  another  of  those  LONG
 *   documentation files.  I was tired of those STACKS of listings, etc.,
 *   that  gathered  dust  simply  because they  were too much trouble to
 *   handle  once I printed  them.   Now  the  printed listings are small
 *   enough to keep in notebooks.   As a bonus, CRAM is especially useful
 *   for printing program listings.  The reduced format is just the thing
 *   to show program structure and the BIG picture!
 *
 *   While not limited to Epson printers,  it is hardcoded for my FX-86e.
 *   Of course you can provide your own setup and un-setup codes for your
 *   printer, and include them in a printer setup file for CRAM to use.
 *
 *   USAGE:
 *   
 *   CRAM srcfile crammedfile [/options]
 *
 *   where [/options] are:
 *          /COLUMN=n       with 1 ó n ó 10
 *          /DUAL           automatically format dual-sided pages
 *          /EPSON          default printer
 *          /EVEN           prints EVEN pages only
 *          /FF             if <FF> encountered, align to next logical page
 *          /HPLJ           select HPLJ printer
 *          /LARGE          use PICA format to get 137 chars/line, EPSON only
 *          /NONE           use to have CRAM function as a column program only
 *          /ODD            prints ODD pages only
 *          /PAGELENGTH=n   may vary printed page length from 1-154 lines
 *          /SKIP=n         number of columns to ignore in srcfile
 *          /TAB=n          number of columns representing a tab
 *          /TEST           test the options on the srcfile, no output files
 *          /WHITE=n        number of characters of whitespace on gutter of page
 *          /WIDTH=n        width of CRAM's page, normally 160
 *   
 *   The  srcfile  should  be  a  valid DOS filename;  wildcards  are not
 *   accepted.   You  need  only  supply  enough  to the  option  name to
 *   distinguish it from the other options, for example /C=1 and /N.
 *   
 *   As a daily VAX user,  I  have tried to implement the straightforward
 *   command-line interface that VMS affords the user.  While the srcfile
 *   and crammedfile must be in that order,  the options may be spread at
 *   will along the command line.
 *
 *   The options are much easier understood after a few practice sessions.
 *   '/COLUMN=n' is  used whenever you want more or less than the standard
 *   two columns on each page.   While  normally  defaulted  to two-column
 *   operation, only the first 79 characters on each line (unless some are
 *   /SKIP-ped) are  visible.   '/FF' respects  embedded  formfeeds in the
 *   text.   Normally off, this option moves to the next logical page when
 *   encountered.   The pagelength is adjustable  through  '/PAGELENGTH=n'
 *   The default is 154 rows,  which allows two pages in each column since
 *   most formatters place 66 lines per page.  If you want your one-column
 *   printing shifted right on the page so that you have whitespace on the
 *   gutter  of the page,  then '/WHITE'  is  just  the ticket.   Finally,
 *   '/SKIP=n' ignores the first 'n' characters on each input line.   This
 *   allows you to print 79 useful characters in each column when printing
 *   formatted files  with spaces or tabs in the first 'n' columns.   It's
 *   your job to try the rest.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/*  G L O B A L   D E F I N E S  */
#define byte         unsigned char
#define MAX_CHAR     160
#define MAX_ROWS     154
#define MAX_ELEMENTS 10
#define PICA         137
#define ELITE        160
enum    OE           { ALL,  ODD,   EVEN };
enum    PR           { NONE, EPSON, HPLJ };

/*  E R R O R   C O D E S  */
#define FILENAME_TROUBLE    0x101
#define INVALID_OPTION      0x102
#define MISSING_FILE        0x103
#define INVLD_NUM_COLUMNS   0x104
#define INVLD_TAB_LENGTH    0x105
#define NO_DISK_SPACE       0x106

/*  S E V E R I T Y   C O D E S  */
#define FATAL         "F"
#define SEVERE        "S"
#define WARNING       "W"
#define INFORMATIONAL "I"

/*  G L O B A L   D A T A  */ 
char    BUFFER[MAX_ROWS][MAX_CHAR+1];
char    PRINTER_INIT[200];
char    PRINTER_TERM[200];
char    SOURCE[120];
int     SKIP_PT     = 0;
int     NUM_COLS    = 2;
int     NUM_CHAR    = MAX_CHAR;
int     NUM_ROWS    = MAX_ROWS;
int     WHITE       = 0;
int     TAB         = 8;
int     DISPLAYABLE;
byte    NUM_FILES   = 0;
byte    FF          = 0;
byte    DUAL        = 0;
byte    LEFT_EDGE;
byte    TEST        = 0;
int     PAGE_COUNT  = 0;
int     LINE_WIDTH  = 0;
long    LINE_COUNT  = 0;
int     FF_FOUND    = 0;
enum    OE ODD_EVEN = ALL;
enum    PR FORMAT   = EPSON;
char    *INFILE = 0, *OUTFILE = 0;
FILE    *INPUT,  *OUT_ODD, *OUT_EVN;

void    _nullcheck();
void    _setenvp();
void    open_files();
void    format_page();
void    print_copyright();
void    filter_input();
void    get_printer_defaults();
void    parse_command_line();
void    get_filenames_and_options();
void    err_exit();

/* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */

int     main( argc, argv )

int     argc;
char    *argv[];
{
        int     i, value;
        char    equivalence[100], parsed[120], *elements[MAX_ELEMENTS];
        char    *option, *parse_option(), *status;

        /* Tell them who I am. */
        print_copyright();

        /* Parse the command line for files and switches. */
        memset( equivalence, '\0', sizeof( equivalence ) );
        memset( parsed, '\0', sizeof( parsed ) );
        memset( SOURCE, '\0', sizeof( SOURCE ) );
        strcpy( SOURCE, argv[0] );
        i = '\\';
        if ( ( status = strrchr( SOURCE, i ) ) == 0 )
            SOURCE[0] = 0;
        else
            *(status+1)= 0;
        for ( i = 1; i < argc; i++ ){
            strcat( equivalence, argv[i] );
            strcat( equivalence, " " );
        };
        value = MAX_ELEMENTS;
        parse_command_line( equivalence, parsed, elements, &value );
        get_filenames_and_options( elements, &value, &INFILE, &OUTFILE );
        get_printer_defaults();

        /* Open the proper files for reading/writing. */
        open_files();

        /* Send the printer setup string. */
        if ( ! TEST ) {
            fputs( PRINTER_INIT, OUT_ODD );
            fputs( PRINTER_INIT, OUT_EVN );
        };

        /* Read from the input file and build the output file in memory. */
        format_page();

        /* Send the printer termination string and close all files. */
        if ( ! TEST ) {
            fputs( PRINTER_TERM, OUT_ODD );
            fputs( PRINTER_TERM, OUT_EVN );
            fflush( OUT_ODD );
            fflush( OUT_EVN );
            fclose( OUT_ODD );
            fclose( OUT_EVN );
        };
        fclose( INPUT );
        return( 0 );
}

/* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */

void    open_files()
{
        int     i;
        char    *name, autoname[100] = "\0";

        /* We always have an input file to open. */
        INPUT = fopen( INFILE,  "rt" );

        /* Flag any pesky filename errors. */
        if ( !INPUT )
            err_exit( FILENAME_TROUBLE, FATAL );

        /* If we are just testing, 'return to sender'. */
        if ( TEST ) return;

        /* Figure out the proper output file(s) to open. */
        if ( DUAL || ODD_EVEN != ALL ) NUM_FILES = 1;
        switch ( NUM_FILES ) {
            case 2 :
                OUT_ODD = OUT_EVN = fopen( OUTFILE, "wt" );
                break;
            case 1 :
                if ( OUTFILE ) strcpy( autoname, OUTFILE );
                strcat( autoname, INFILE );
                i = '.';
                name = strchr( autoname, i );
                if ( ! name ) name = autoname + strlen( autoname );
                if ( DUAL ) {
                    strcpy( name, ".ODD" );
                    OUT_ODD = fopen( autoname, "wt" );
                    strcpy( name, ".EVN" );
                    OUT_EVN = fopen( autoname, "wt" );
                    }
                else {
                    strcpy( name, ".CRM" );
                    OUT_ODD = OUT_EVN = fopen( autoname, "wt" );
                };
                break;
        };

        /* Flag any pesky filename errors. */
        if ( !OUT_ODD || !OUT_EVN )
            err_exit( FILENAME_TROUBLE, FATAL );
}

/* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */

void    format_page()
{
        int     i, j, x, carryover, value, lr, current_col, status;
        long    position, lc, tlc;
        char    *file_status, line[MAX_CHAR+20];
        FILE    *output;

        carryover = lc = tlc = 0;
        do {
            PAGE_COUNT++;
            LEFT_EDGE = ( !(PAGE_COUNT & 1) && ( DUAL || (ODD_EVEN == EVEN) ) ) ? 0 : WHITE;
            memset( BUFFER, ' ', NUM_ROWS * (MAX_CHAR+1) );
            for ( i=0; i < NUM_ROWS; i++ ) BUFFER[i][NUM_CHAR] = 0;
            for ( current_col=1, lr=0; current_col<=NUM_COLS; current_col++ ) {
              if ( carryover ) {
                filter_input( &line[0] );
                x = strlen( line );
                if ( x > LINE_WIDTH ) LINE_WIDTH = x;
                line[DISPLAYABLE] = '\0';
                lc++;
                if ( current_col != NUM_COLS )
                    strncpy( &BUFFER[0][LEFT_EDGE], &line[0], strlen( line ) );
                else
                    strcpy( &BUFFER[0][LEFT_EDGE], &line[0] );
              };
              for ( i=carryover; i < NUM_ROWS; i++ ) {
                carryover = 0;
                position = ftell( INPUT );
                file_status = fgets( line, 159, INPUT );
                if ( file_status == NULL ) break;
                if ( strstr( &line[1], "\f" ) ) {
                  for ( j = 1; line[j] != '\f'; j++ ) {};
                  fseek( INPUT, (position+j), SEEK_SET );
                  line[j] = '\0';
                };
                if ( line[0] == '\f' ) {
                    FF_FOUND++;
                    if ( lc > LINE_COUNT ) LINE_COUNT = lc;
                    tlc += lc;
                    lc   = 0;
                    if ( FF ) {
                      if ( i <= (NUM_ROWS+1)/2 )
                        i = carryover = (NUM_ROWS+1)/2;
                      else  {
                        carryover = 1; break;
                      };
                    };
                };
                filter_input( &line[0] );
                x = strlen( line );
                if ( x > LINE_WIDTH ) LINE_WIDTH = x;
                line[DISPLAYABLE] = '\0';
                lc++;
                if ( current_col != NUM_COLS )
                    strncpy( &BUFFER[i][LEFT_EDGE], &line[0], strlen( line ) );
                else
                    strcpy( &BUFFER[i][LEFT_EDGE], &line[0] );
              };
              if ( i > lr ) lr = i;
              if ( file_status == NULL ) break;
              LEFT_EDGE = LEFT_EDGE + DISPLAYABLE + 1;
              if ( carryover > 1 ) carryover = 0;
            };

            /* Write out a page buffer and get ready for next page. */
            if ( TEST ) {
                if ( lc > LINE_COUNT ) LINE_COUNT = lc;
                continue;
            };
            if ( ( ODD_EVEN == ALL  ) ||
               ( ( ODD_EVEN == ODD  ) &&  ( PAGE_COUNT & 1 ) ) ||
               ( ( ODD_EVEN == EVEN ) && !( PAGE_COUNT & 1 ) ) ) {
              output = ( PAGE_COUNT & 1 ) ? OUT_ODD : OUT_EVN;
              for ( i=0; i < lr; i++ ) {
                j = strlen( &BUFFER[i][0] );
                if ( j )
                  if ( BUFFER[i][j-1] == '\n' ) j--;
                if ( j > (NUM_CHAR-1) )  j = NUM_CHAR - 1;
                BUFFER[i][j]   = '\n';
                BUFFER[i][j+1] = '\0';
                status = fputs( &BUFFER[i][0], output );
              };
              if ( status == EOF ) err_exit( NO_DISK_SPACE, SEVERE );
              if ( file_status != NULL ) fputs( "\xc\0", output );
            };
        } while ( file_status );

        if ( TEST ) {
            tlc += lc;
            fprintf( stderr, "\tForm Feeds ... %d\n", FF_FOUND );
            fprintf( stderr, "\tPage Count ... %d\n", PAGE_COUNT );
            fprintf( stderr, "\tLine Count ... %ld (maximum input page length is %ld)\n", tlc, LINE_COUNT );
            fprintf( stderr, "\tLine Width ... %d (maximum %d displayable)\n", LINE_WIDTH, DISPLAYABLE );
        };
        return;
}

/* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */

void    _nullcheck () {};
void    _setenvp () {};


/* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */

void    print_copyright()
{
        fprintf( stderr, "CRAM  -  ASCII File Reducer  -  V3.3\n" );
        fprintf( stderr, "Copyright (c) 1988-93  -  Dean Tutterow  -  All Rights Reserved\n" );
}

/* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */

void    filter_input( line )

char	*line;
{
	int 	i, n;
        char    newline[MAX_CHAR+20], *oldline;

	/* Clean all tabs and formfeeds from the input line. */
	oldline = line;
        memset( newline, '\0', sizeof( newline ) );
        for ( i = 0; i <= MAX_CHAR+1; ) {
          if ( *line == '\0' ) { newline[i] = '\0'; break; }
          if ( *line == '\f' ) { line++; continue; }
          if ( *line == '\n' ) *line = ' ';
          if ( *line == '\t' ) {
	    n = i;
            while ( n >= 0 )
              n -= TAB;
	    n = -n;
            strncpy( &newline[i], "        ", n );
            i += ( n - 1 ); }
          else
            newline[i] = *line;
	  i++;
          line++;
        };
        if ( newline[0] != 0x0D )
          strcpy( oldline, &newline[SKIP_PT] );
	else
          strcpy( oldline, &newline[0] );
}

/* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */

void    get_printer_defaults()
{
        char    *file_status, filename[120];
        float   x;

        if ( FORMAT == NONE ) return;
        strcpy( filename, SOURCE );
        strcat( filename, "CRAM.DAT" );
        INPUT = fopen( filename, "rt" );
        if ( !INPUT ) {
          switch ( FORMAT ) {

            case EPSON:
                 if ( NUM_CHAR == PICA )
                     strcpy( PRINTER_INIT, "\x1b\x33\xf\x1b\x53\x30\xf\0" );
                 else
                     strcpy( PRINTER_INIT, "\x1b\x33\xf\x1b\x53\x30\x1b\x4d\xf\0" );
                 strcpy( PRINTER_TERM, "\x1b\x32\x1b\x54\x1b\x50\x12\x1a\0" );
                 break;

            case HPLJ:
                 /* Compute vertical scale factor for line spacing. */
                 x = 48.0 / ( (NUM_ROWS+2) / 10.0 );
                 sprintf( PRINTER_INIT, "\x1b(1006X\x1b&l%2.3fC", x );
                 sprintf( PRINTER_TERM, "\x1b(1012X\x1b&l6D" );
                 break;
          };
          return;
        }
        file_status = fgets( PRINTER_INIT, sizeof( PRINTER_INIT ), INPUT );
        if ( file_status == NULL ) goto file_error;
        file_status = fgets( PRINTER_TERM, sizeof( PRINTER_TERM ), INPUT );
        if ( file_status == NULL ) goto file_error;
        file_error:
        fclose( INPUT );
        return;
}


/* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */

char    *parse_option( option, output )

char    *option, **output;
{
        int offset;

        if ( *option != '/' ) return( 0 );
        if ( !( offset = strcspn( option, "=:" ) ) ) return( 0 );
        if ( offset == strlen( option ) ) return( 0 );
        *output = option + offset + 1;
        return( (char *)output );
}

/* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */

void    parse_command_line( input, output, arrayed, number )

char	*input, *output, *arrayed[];
int	*number;
{
	int 	i, j, k, paren;
        char    temp[100], *env;

        /* Get any parameters which may be in the CRAM environment variable. */
        temp[0] = (int) env = 0;
        env = getenv( "CRAM" );
        if ( env ) {
            strcpy( temp, env );
            strcat( temp, " " );
        };
        strcat( temp, input );

        /* Convert input line to upper case */
        for ( i=0; i < strlen( temp ); i++ )
          (temp)[i] = toupper( (temp)[i] );

        (output)[0] = ' ';

        for ( i=k=paren=0, j=1;( i < strlen( temp ) && k <= *number ); i++ ) {
          switch ( (temp)[i] ) {
            case ','  : if ( paren ) { (output)[j] = (temp)[i]; break; }
            case ' '  :
            case '\t' : if ( (output)[j-1]!=' ' ) (output)[j]   = ' '; break;
            case '/'  : if ( (output)[j-1]!=' ' ) (output)[j++] = ' '; paren--;
            case '('  : paren += 2;
            case ')'  : paren--;
            default   : (output)[j] = (temp)[i];
	  }
	  j++;

          /*
          ** If this is the start of an argument, then place its address
          ** in the argument list & null-terminate the previous argument.
          */
          if ( (output)[j-1] != ' ' && (output)[j-2] == ' ' ) {
            if ( k != *number ) {
              (arrayed)[k] = &(output)[j-1];
	      k++; 
            };
            (output)[j-2] = '\0';
          };
        };
        (output)[j] = '\0';
	*number = k;
};

/* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */

void    get_filenames_and_options( elements, value, INFILE, OUTFILE )

char	**INFILE, **OUTFILE, *elements[];
int	*value;
{
	int	first, i;
        char    *status, *option, *parse_option(), first_char;

        for ( i = 0, first = 1; i < *value; i++ ) {
          if ( *elements[i] != '/' ) {
            if ( first ) { *INFILE = elements[i]; first = 0; }
	    else *OUTFILE = elements[i];
	    NUM_FILES++; }
	  else {
            first_char = *(elements[i] + 1);
            switch ( first_char ) {

                case 'C' :      /* # OF COLUMNS PARAMETER */
                        status = parse_option( elements[i], &option );
                        if ( status ) sscanf( option, "%d", &NUM_COLS );
                        else NUM_COLS = 2;
                        if ( NUM_COLS > 10  || NUM_COLS < 1 )
                            err_exit( INVLD_NUM_COLUMNS, FATAL );
			break;

                case 'D' :      /* DUAL - PRINT ODD/EVEN PAGES */
                        DUAL = 1;
                        ODD_EVEN = ALL;
                        break;

                case 'E' :
                        first_char = *(elements[i] + 2);
                        switch ( first_char ) {
                            case 'P':   /* EPSON PRINTER */
                                  FORMAT = EPSON;
                                  break;

                            case 'V':   /* PRINT EVEN PAGES ONLY */
                                  if ( ! DUAL ) ODD_EVEN = EVEN;
                                  break;
                        };
                        break;

                case 'F' :      /* RECOGNIZE FORMFEEDS */
			FF = 1;
			break;

                case 'H' :      /* HPLJ PRINTER */
                        FORMAT   = HPLJ;
                        NUM_CHAR = MAX_CHAR;
                        break;

                case 'L' :      /* LARGE-SMALL PRINT( CHARS/LINE = 137 )*/
                        if ( FORMAT == EPSON ) NUM_CHAR = PICA;
                        break;

                case 'N' :      /* NO PRINTER FORMATTING */
                        FORMAT = NONE;
			break;

                case 'O' :      /* PRINT ODD PAGES ONLY */
                        if ( ! DUAL ) ODD_EVEN = ODD;
			break;

                case 'P' :      /* LINES-PER-PAGE PARAMETER */
                        status = parse_option( elements[i], &option );
                        if ( status ) sscanf( option, "%d", &NUM_ROWS );
			else NUM_ROWS = MAX_ROWS;
                        if ( NUM_ROWS > MAX_ROWS ) NUM_ROWS = MAX_ROWS;
                        break;

		case 'S' :	/* START INPUT PROCESSING AT THIS POSITON */
                        status = parse_option( elements[i], &option );
                        if ( status ) sscanf( option, "%d", &SKIP_PT );
			else SKIP_PT = 0;
			break;

                case 'T' :
                        first_char = *(elements[i] + 2);
                        switch ( first_char ) {
                            case 'A':   /* TAB INCREMENT */
                                status = parse_option( elements[i], &option );
                                if ( status ) sscanf( option, "%d", &TAB );
                                else TAB = 8;
                                if ( TAB > 8 || TAB < 1 ) {
                                    err_exit( INVLD_TAB_LENGTH, WARNING );
                                    TAB = 8;
                                };
                                break;

                            case 'E' :  /* TEST SOURCE FILE */
                                TEST = 1;
                                break;
                        };
                        break;

                case 'W' :      
                        first_char = *(elements[i] + 2);
                        switch ( first_char ) {
                            case 'H':   /* WHITE SPACE ON GUTTER */
                                status = parse_option( elements[i], &option );
                                if ( status ) sscanf( option, "%d", &WHITE );
                                else WHITE = 35;
                                if ( WHITE > MAX_CHAR/2 ) WHITE = 35;
                                break;

                            case 'I':   /* WIDTH OF PAGE */
                                status = parse_option( elements[i], &option );
                                if ( status ) sscanf( option, "%d", &NUM_CHAR );
                                else NUM_CHAR = MAX_CHAR;
                                if ( NUM_CHAR > MAX_CHAR ) NUM_CHAR = MAX_CHAR;
                                break;
                        };
                        break;

                default : 
                        err_exit( INVALID_OPTION, WARNING );
            };
          };
        };

        /* Compute the maximum displayable portion of the input line. */
        DISPLAYABLE = ( NUM_CHAR - NUM_COLS + 1 - WHITE ) / NUM_COLS;
}

/* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */
 
void    err_exit( code, severity )

int	code;
char	*severity;
{
	char 	err_code[20], msg[50];

	/* Display an error message, delete output file, exit. */
        switch ( code ) {

	  case FILENAME_TROUBLE :
               strcpy( msg, "Trouble opening files, please check filenames." );
               strcpy( err_code, "FILEOPEN" );
               break;

	  case MISSING_FILE :
               strcpy( msg, "You must specify an input and output file." );
               strcpy( err_code, "MISSFILE" );
               break;

	  case INVALID_OPTION :
               strcpy( msg, "You have specified an invalid option." );
               strcpy( err_code, "INVLDOPT" );
               break;

          case INVLD_NUM_COLUMNS :
               strcpy( msg, "You have specified an invalid number of columns." );
               strcpy( err_code, "IVLDCOLS" );
               break;

          case INVLD_TAB_LENGTH :
               strcpy( msg, "Tab length must be between 1 and 8 characters." );
               strcpy( err_code, "IVLDTAB" );
               break;

          case NO_DISK_SPACE :
               strcpy( msg, "Not enough disk space for output file(s)." );
               strcpy( err_code, "NODSKSPC" );
               break;

          default : 
               strcpy( msg, "Unknown error!" );
               strcpy( err_code, "UNKNOWN" );
        };

        fprintf( stderr, "\nCRAM-%c-%s, %s\n", *severity, err_code, msg );

        if ( *severity == 'F' ) {
          fprintf( stderr, "\n  usage: CRAM infile outfile [options]\n\n" );
          fprintf( stderr, "  where [options] and defaults are:\n" );
          fprintf( stderr, "\t[/column=N]      2, 1 <= N <= 10\n" );
          fprintf( stderr, "\t[/dual]          off, on automatically formats dual-sided pages\n" );
          fprintf( stderr, "\t[/epson]         EPSON, default printer\n" );
          fprintf( stderr, "\t[/even]          off, on prints even pages only\n" );
          fprintf( stderr, "\t[/ff]            off, on recognizes form feeds\n" );
          fprintf( stderr, "\t[/hplj]          off, on selects HPLJ printer\n" );
          fprintf( stderr, "\t[/large]         off, on selects large EPSON print\n" );
          fprintf( stderr, "\t[/none]          off, on selects no printer formatting\n" );
          fprintf( stderr, "\t[/odd]           off, on prints odd pages only\n" );
          fprintf( stderr, "\t[/pagelength=N]  154, 1 <= pagelength <= 154\n" );
          fprintf( stderr, "\t[/skip=N]        0, # of chars to skip on each input line\n" );
          fprintf( stderr, "\t[/tab=N]         8, # of chars representing a tab\n" );
          fprintf( stderr, "\t[/test]          test the options on the srcfile\n" );
          fprintf( stderr, "\t[/white=N]       0, # of chars of white space for gutter\n" );
        };

        /* Close streams and delete partial output for TERMINAL failures. */
        if ( ( *severity == 'F' ) || ( *severity == 'S' ) ) {
          fflush( OUT_ODD );
          fflush( OUT_EVN );
          fclose( INPUT   );
          fclose( OUT_ODD );
          fclose( OUT_EVN );
          unlink( OUTFILE );
	
	  /* Exit with errorlevel 1 */
          exit( 1 );
        };
}
