/*
 *                      PackHdr_c
 *                      ~~~~~~~~~
 *
 *  Pack C header files
 *
 *  The purpose of this program is to take a C header file and to
 *  remove all comments and redundant white space.   Although this will
 *  make the file extremely difficult for humans to read, it will reduce
 *  the space used, and speed up compilations.
 *
 *  It is expected that normal practise will to be to keep a nicely
 *  formatted version of header files that are fully commented on the
 *  development/source disks for C68, but that the copies of the header
 *  files on the runtime disk will all have been processed to save both
 *  time and space.
 * *
 * (c) Copyright 1993 David J. Walker
 *     Email:  d.j.walker@slh0101.wins.icl.co.uk
 *
 *     Permission to copy and/or distribute granted under the
 *     following conditions:
 *
 *     1). This notice must remain intact.
 *     2). The author is not responsible for the consequences of use
 *         this software, no matter how awful, even if they
 *         arise from defects in it.
 *     3). Altered version must not be represented as being the
 *         original software.
 *
 *  AMENDMENT HISTORY
 *  ~~~~~~~~~~~~~~~~~
 *
 *  01 May 93   DJW v1.0    Initial version
 *
 *  06 Nov 93   DJW v1.1  - Remove whitespace that immediately follows a
 *                          punctation character
 *
 *  30 Jan 94   DJW v1.2  - Fix handling of quote characters when preceded by
 *                          a backslash to escape them.
 *                          Problem reported by David Gilham
 */

#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <qdos.h>

char _prog_name[] = "PackHdr";
char _version[]   = " v1.2 ";
char _copyright[] = "C68 Header Packer";
char copyright[]  = "(c)1993, 1994  D.J.Walker";
void (*_consetup)() = consetup_title;

FILE    *fpin, *fpout;
int     ch, ch2;
short   newline = 0;
short   whitespace = 0;     /* Set if processing whitespace */
short   singlequote = 0;    /* Set if processing a single-qioted string */
short   doublequote = 0;    /* Set if processing a double-quoted string */
short   escaped = 0;        /* Set if previous character was 'escape' character */
short   comment = 0;        /* Set if processing a comment */
short   punctuation = 0;
long    readcount = 0;
long    writecount = 0;


/*
 *  Output the current character
 *  (Checking for error)
 */
static
void write_char (void)
{
    if (putc(ch,fpout) == EOF) {
        (void) fprintf(stderr, "Error writing output file\n");
        exit(-1);
    }
    writecount++;
    return;
}

/*
 *  Normal processing for a character
 */
void process_char (void)
{
    /*
     *  If inside a comment simply ignore
     */
    if (comment != 0) {
        return;
    }
    /*
     *  If inside a string, then we better
     *  write it out!
     */
    if (singlequote != 0 || doublequote != 0) {
        write_char();
        return;
    }
    /*
     *  If it whitespace, then simply
     *  remember that we have seen it
     *  (unless character was punctuation
     *   so we can ignore white space.)
     */
    if (isspace(ch)) {
        if (punctuation == 0) {
            whitespace++;
        }
        return;
    }
    /*
     *  If there is a pending newline, then
     *  output it before this cahracter
     */
    if (newline) {
        (void) putc('\n', fpout);
        writecount++;
        newline = 0;
        whitespace = 0;
    }
    punctuation = (ch == ',');
    /*
     *  If there is pending whitespace, and
     *  this character is not a punctation
     *  character, then output a single space
     */
    if (whitespace) {
        if (punctuation == 0) {
            (void) putc(' ', fpout);
            writecount++;
        }
        whitespace = 0;
    }
    write_char();
    return;
}

/*
 *  Treat as default character case
 */
void default_char (void)
{
    escaped = 0;
    process_char();
}

void main(argc, argv)
int     argc;
char    *argv[];
{
    if (argc != 3) {
        (void) fprintf(stderr,"\nUsage:\n\t%s source_file target_file\n", _prog_name);
        exit(1);
    }

    if ((fpin=fopen(argv[1],"r")) == NULL) {
        (void) fprintf(stderr,"%s:  Failed to open input file '%s'\n",_prog_name, argv[1]);
        exit(2);
    }

    if ((fpout=fopen(argv[2],"w")) == NULL) {
        (void) fprintf(stderr,"%s:  Failed to open output file '%s'\n", _prog_name, argv[2]);
        exit(2);
    }

    while ((ch=getc(fpin)) != EOF) {
        readcount++;
        switch (ch) {
        /*
         *  Forward quote:
         *  Could be start of a comment.
         *  If not treat as data character
         */
        case '/':
            /*
             *  If this character is not followed by *
             *  then it cannot be the start of a comment
             */
            if ((ch2=getc(fpin)) != '*') {
                (void) ungetc(ch2,fpin);
                default_char();
                break;
            }
            /*
             *  It looks like it was the start
             *  of a comment after all.  We had
             *  better ignore the comment.
             */
            comment++;
            break;

        /*
         *  asterisk:
         *  Could be end of a comment
         */
        case '*':
            if ((ch2=getc(fpin)) != '/') {
                (void) ungetc(ch2,fpin);
                default_char();
                break;
            }
            if (comment) {
                comment = 0;
                punctuation = 0;
                whitespace = 1;
                break;
            }
            /* should we ever get here ! */
            (void) fprintf(stderr, "Unexpected END-OF COMMENT at %ld\n",readcount);
            (void) ungetc(ch2,fpin);
            default_char();
            break;

        /*
         *  doublequote:
         *  Could be start or end of a string
         */
        case '"':
            /*
             *  If inside a comment simply ignore
             */
            if (comment != 0) {
                escaped = 0;
                break;
            }
            /*
             *  If we are inside a singlequote string or
             *  a comment, then treat character as data
             */
            if (singlequote != 0  || comment != 0 ) {
                write_char();
                escaped = 0;
                break;
            }
            /*
             *  If we are already inside a doublequote
             *  string, then this is the end of the string
             *  (unless it is preceeded by an escape)
             */
            if (doublequote) {
                if (escaped == 0) {
                    doublequote = 0;
                }
                write_char();
                escaped = 0;
                break;
            }
            /*
             *  It looks like this is the start
             *  of a doublequoted new string.
             */
            doublequote++;
            default_char();
            break;

        /*
         *  Single-quote:
         *  Could be start or end of a string
         */
        case '\'':
            /*
             *  If inside a comment simply ignore
             */
            if (comment != 0) {
                escaped = 0;
                break;
            }
            /*
             *  If we are inside a doublequote string or
             *  a comment, then treat character as data
             */
            if (doublequote != 0  || comment != 0 ) {
                write_char();
                escaped = 0;
                break;
            }
            /*
             *  If we are already inside a singlequote
             *  string, then this is the end of the string
             *  (unless it is escaped)
             */
            if (singlequote) {
                if (escaped == 0) {
                    singlequote = 0;
                }
                write_char();
                escaped = 0;
                break;
            }
            /*
             *  It looks like this is the start of
             *  a singlequoted new string.
             */
            singlequote++;
            default_char();
            break;

        /*
         *  End of a line
         *  Redundant lines are deleted
         */
        case '\n':
            /*
             *  If inside a comment simply ignore
             */
            if (comment != 0) {
                break;
            }
            newline++;
            break;

        /*
         *  Escape character
         */
        case '\\':
            escaped++;
            process_char();
            break;

        default:
            default_char();
            break;

        } /* End of switch */
    } /* End of While loop */
    if (newline) {
        (void) putc('\n', fpout);
        writecount++;
    }

    if (! feof(fpin)) {
        (void) fprintf (stderr, "**** Error (%d) reading input file ****\n", ch);
        (void) fprintf (stderr, "\t\t(errno=%d, _oserr=%d\n", errno, _oserr);
    }
    (void) fclose (fpin);
    (void) fclose (fpout);
    (void) fprintf (stderr, "Characters read=%ld,  written=%ld\n", readcount, writecount);
    exit(0);
}


