# $Copyright:	$
# Copyright (c) 1984, 1985, 1986, 1987, 1988, 1989, 1990 
# Sequent Computer Systems, Inc.   All rights reserved.
#  
# This software is furnished under a license and may be used
# only in accordance with the terms of that license and with the
# inclusion of the above copyright notice.   This software may not
# be provided or otherwise made available to, or used by, any
# other person.  No title to or ownership of the software is
# hereby transferred.
#!nawk -f
# 
# kextract - A cmn_err tool to extract comments from source files.
#		Assumes we're using files that we've already checked with klint.
#
#	Can be invoked as:
#	1)	nawk -f kextract FILE
#	2)	kextract FILE
#
#	Variables:
#
#	current - The current filename.
#	line - The line number in the current file.
#	parans - A count of the number of levels deep in ()
#
#	Flags:
#	comment - Check to see if we're in a comment flag
#	debug - Check to see if we're in side #ifdef DEBUG code.
#	named - The filename has been printed.
#	printEh - We've printed the .Eh macro already for this line.
#			(handles multiple quotes in one line.)
#	
#

BEGIN {
	current = FILENAME;
	line = 1;
	parans = 0;
	comment = 0;
	debug = 0;
	named = 0;
	printEh = 1;
}

# A new file - reset the variables.
{
	if (current != FILENAME) {
		parans = 0;
		line = 1;
		comment = 0;
		current = FILENAME;
		debug = 0;
		named = 0;
	}
}
	
# filter blank lines
/^[ 	]*$/		{ ++line; next;		}

#  No parsing is needed during some #ifdef 's
/#if/ {
	if (index($1, "#if") && debug) {
		debug++;
	}
#  NOTE:  If you update this list here, you need to update it in klint too!
	if (! debug && (index($0, "DEBUG") || index($0, "MFG") || index($0, "CHECKSLIC") || index($0, "WATCHPOINT") || index($0, "ns32000") || index($0, "MAX_PROC_ADDR_MEM") || index($0, "KLINT") || index($0, "KWATCHPT"))) {
		debug++;
	}
}

/#endif/ {
	if (index($1, "#endif") && debug) {
		debug--;
	}
}

#If we're in a comment, don't check the input.
/\/\*/  {
	comment = 1;
}

# check to see if we're in a comment, and get out of it.
{
	if (index($0, "*/")) {
		comment = 0;
		$0 = substr($0, index($0, "*/")+1, length($0) - index($0, "*/"));
	}
}

# parse cmn_err function calls
/cmn_err\(/	{ 
	if (! comment && ! debug && ! index($0, "CE_CONT")) {
		gettype($0);
		printname(current);
		parans = 0;
		while (getparans($0) != 0) {
			nextline();
			if (index($1, "cmn_err("))
				parans = 0;
		}
		printEh = 1; printf("\n");
		if ( ! iscorrect($0)) {
			perror($0,"Malformed comment");
		}
	}
}

	
# parse CALL cmn_err in assembly code
/CALL[ 	]*cmn_err/	{ 
	if (! comment && ! debug ) {
		type = "PANIC";
		printname(current);
		getparans($0);
		printEh = 1; printf("\n");
		if ( ! iscorrect($0)) 
			perror($0,"Malformed comment");
	}
}


# parse ASSERT function calls
/ASSERT\(/	{ 
	if (! comment && ! debug) {
		type = "PANIC";
		printname(current);
		parans = 0;
		while (getparans($0) != 0) {
			nextline();
			if (index($1, "ASSERT("))
				parans = 0;
		}
		printEh = 1; printf("\n");
		if ( ! iscorrect($0)) {
			perror($0, "Malformed Comment");
		}
	}
}

#  Parse error table entries.  Only report an error on a mis commented
#  entry.  There are other strings that match this pattern which don't
#  require a special comment.

/^[	 ]+".*",$/	{
	if (! comment && ! debug && ! index($0, "CE_CONT")) {
		type = "TABLE";
		string = skip($0, index($0, "\""));
		ind = index(string,"\"");
		if (com = checkcorrect($0, a)) {
			printname(current);
			printstr(string, ind);
			printEh = 1; printf("\n");
			for (i = 1; i <= com ; i++)
				printcom(a[i]);
		}
	}
}

# Update the line count, and go to the next.
{ ++line; next;		}

END {
}

function gettype(str)
{
	if (index($0,"CE_TO_USER")) {
		type = "TO USER";
	} else if (index($0,"CE_NOTE")) {
		type = "NOTE";
	} else if (index($0,"CE_WARN")) {
		type = "WARNING";
	} else if (index($0,"CE_PANIC")) {
		type = "PANIC";
	}
}
		
# iscorrect(str)
#  Checks a comment block after a cmn_err or ASSERT call to make sure it
#	is properly formated.
#
function iscorrect(str)
{
	if (nextline()) {
		if (index($1,"/*") && length($2)  == 0)  {
			nextline();
		} else {
			return 0;
		}
		if (index($1,"*+")) {
			printcom($0);
			nextline();
		} else {
			return 0;
		}
		while (index($1,"*+")) {
			printcom($0);
			nextline();
		}
		if (index($1,"*/") && length($2)  == 0)  {
		} else {
			return 0;
		}
		return 1;
	} else return 0;
}

# checkcorrect()
#  Checks if the comment is correct, and puts the comment in an array
#	returns the number of items in the array.
#
function checkcorrect(str, a)
{
	i = 1;
	if (nextline()) {
		if (index($1,"/*") && length($2)  == 0)  {
			nextline();
		} else {
			return 0;
		}
		if (index($1,"*+")) {
			a[i] = $0;
			nextline();
		} else {
			return 0;
		}
		while (index($1,"*+")) {
			i++;
			a[i] = $0;
			nextline();
		}
		if (index($1,"*/") && length($2)  == 0)  {
		} else {
			return 0;
		}
		return i;
	} else return 0;
}

# nextline()
#  Gets the next line of input, and updates the line number.
#
function nextline()
{
	if (getline) {
		line++;
		return 1;
	} else {
		return 0;
	}
}

# printname()
#  Prints the filename to the output file in the special format
#
function printname(str)
{
	if (! named) {
		printf("+++++ %s\n",str);
		named = 1;
	}
}

# printstr()
#  Prints the string associated with this cmn_err
#
function printstr(str, n)
{
	if (n > 0) {
		if (printEh) {
			printf(".Eh \"%s\" \"%s", type, substr(str, 0, n));
			printEh = 0;
		} else
			printf(" \"%s", substr(str, 0, n));
	}
}

# printcom()
#  Prints the comment, inserting a number in place of any leading
#	white space.
#
function printcom(str)
{
	count = 0;
	while (index(str,"*") != 1) {
		if (index(str,"	") == 1)
			count += 8; 
		if (index(str," ") == 1)
			count += 1;
		str = skip(str, 1);
	}
	printf("%d",count);
	printf("%s\n",str);
}
# getparans()
# recursively descends the paran structure within the cmn_err or ASSERT call.
#
function getparans(str)
{
	quote = index(str, "\"");
	open = index(str, "(");
	clse = index(str, ")");


	if (! open && ! clse ) {
		if (quote) {
			str = skip(str, quote);
			ind = index(str,"\"");
			printstr(str, ind);
		}
		return parans;
	}

	if (open && clse) {
		if (open <= clse) {
#			/* The open paran is first. */
			if (quote && (quote <= open)) {
				str = skip(str, quote);
				ind = index(str,"\"");
				printstr(str, ind);
			} else {
				parans++;
				ind = open;
			}
		} else {
#			/* The clse paran is first */
			if (quote && (quote <= clse)) {
				str = skip(str, quote);
				ind = index(str,"\"");
				printstr(str, ind);
			} else {
				parans--;
				ind = clse;
			}
		}
	} else if (open && ! clse) {
#		/* The open paran is first. */
		if (quote && (quote <= open)) {
			str = skip(str, quote);
			ind = index(str,"\"");
			printstr(str, ind);
		} else {
			parans++;
			ind = open;
		}
	} else {
#		/* The clse paran is first */
		if (quote && (quote <= clse)) {
			str = skip(str, quote);
			ind = index(str,"\"");
			printstr(str, ind);
		} else {
			parans--;
			ind = clse;
		}
	}

	getparans(skip(str,ind)); 
	return parans;
}

function skip(str, ind)
{
	return substr(str, ind + 1, length(str) - ind ); 
}

# perror()
# A simple nawk replacement for the perror(3) function.
#
function perror(str, error)
{
	printf("%s: line %d: %s\n %s\n", FILENAME, line, error, str);
}
