/*
 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 *
 * "Portions Copyright (c) 1999 Apple Computer, Inc.  All Rights
 * Reserved.  This file contains Original Code and/or Modifications of
 * Original Code as defined in and that are subject to the Apple Public
 * Source License Version 1.0 (the 'License').  You may not use this file
 * except in compliance with the License.  Please obtain a copy of the
 * License at http://www.apple.com/publicsource and read it before using
 * this file.
 *
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
 * License for the specific language governing rights and limitations
 * under the License."
 *
 * @APPLE_LICENSE_HEADER_END@
 */
/*
 * decomment.c
 *
 * Removes all comments and (optionally) whitespace from an input file.
 * Writes result on stdout.
 */

#include <stdio.h>
#include <ctype.h>      /* for isspace */
#include <libc.h>

/*
 * State of input scanner.
 */
typedef enum {
	IS_NORMAL,
	IS_SLASH,               // encountered opening '/'
	IS_IN_COMMENT,          // within / * * / comment
	IS_STAR,                // encountered closing '*'
	IS_IN_END_COMMENT       // within / / comment
} input_state_t;

static void usage(char **argv);

int
main(int argc, char **argv)
{
	FILE *fp;
	char bufchar;
	input_state_t input_state = IS_NORMAL;
	int exit_code = 0;
	int remove_whitespace = 0;
	int arg;

	if (argc < 2) {
		usage(argv);
	}
	for (arg = 2; arg < argc; arg++) {
		switch (argv[arg][0]) {
		case 'r':
			remove_whitespace++;
			break;
		default:
			usage(argv);
		}
	}

	fp = fopen(argv[1], "r");
	if (!fp) {
		fprintf(stderr, "Error opening %s\n", argv[1]);
		perror("fopen");
		exit(1);
	}
	for (;;) {
		bufchar = getc_unlocked(fp);
		if (bufchar == EOF) {
			break;
		}

		switch (input_state) {
		case IS_NORMAL:
			if (bufchar == '/') {
				/*
				 * Might be start of a comment.
				 */
				input_state = IS_SLASH;
			} else {
				if (!(remove_whitespace && isspace(bufchar))) {
					putchar_unlocked(bufchar);
				}
			}
			break;

		case IS_SLASH:
			switch (bufchar) {
			case '*':
				/*
				 * Start of normal comment.
				 */
				input_state = IS_IN_COMMENT;
				break;

			case '/':
				/*
				 * Start of 'to-end-of-line' comment.
				 */
				input_state = IS_IN_END_COMMENT;
				break;

			default:
				/*
				 * Not the start of comment. Emit the '/'
				 * we skipped last char in case we were
				 * entering a comment this time, then the
				 * current char.
				 */
				putchar_unlocked('/');
				if (!(remove_whitespace && isspace(bufchar))) {
					putchar_unlocked(bufchar);
				}
				input_state = IS_NORMAL;
				break;
			}
			break;

		case IS_IN_COMMENT:
			if (bufchar == '*') {
				/*
				 * Maybe ending comment...
				 */
				input_state = IS_STAR;
			}
			break;


		case IS_STAR:
			switch (bufchar) {
			case '/':
				/*
				 * End of normal comment.
				 */
				input_state = IS_NORMAL;
				break;

			case '*':
				/*
				 * Still could be one char away from end
				 * of comment.
				 */
				break;

			default:
				/*
				 * Still inside comment, no end in sight.
				 */
				input_state = IS_IN_COMMENT;
				break;
			}
			break;

		case IS_IN_END_COMMENT:
			if (bufchar == '\n') {
				/*
				 * End of comment. Emit the newline if
				 * appropriate.
				 */
				if (!remove_whitespace) {
					putchar_unlocked(bufchar);
				}
				input_state = IS_NORMAL;
			}
			break;
		} /* switch input_state */
	}         /* main read loop */

	/*
	 * Done.
	 */
	return exit_code;
}

static void
usage(char **argv)
{
	printf("usage: %s infile [r(emove whitespace)]\n", argv[0]);
	exit(1);
}
