/***********************************************************************
*                                                                      *
*               This software is part of the ast package               *
*           Copyright (c) 1987-2006 AT&T Knowledge Ventures            *
*                      and is licensed under the                       *
*                  Common Public License, Version 1.0                  *
*                      by AT&T Knowledge Ventures                      *
*                                                                      *
*                A copy of the License is available at                 *
*            http://www.opensource.org/licenses/cpl1.0.txt             *
*         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
*                                                                      *
*              Information and Software Systems Research               *
*                            AT&T Research                             *
*                           Florham Park NJ                            *
*                                                                      *
*                 Glenn Fowler <gsf@research.att.com>                  *
*                                                                      *
***********************************************************************/
#pragma prototyped

/*
 * Glenn Fowler
 * AT&T Labs Research
 *
 * nocomment -- strip C comments
 */

#include <ast.h>
#include <ctype.h>

#if 0
static int line_sfputc(int line, Sfio_t* sp, int c)
{
	sfprintf(sp, "<<<C:%d>>>", line);
	return sfputc(sp, c);
}
static int line_sfputr(int line, Sfio_t* sp, const char* buf, int op)
{
	sfprintf(sp, "<<<R:%d>>>", line);
	return sfputr(sp, buf, op);
}
#undef	sfputc
#define sfputc(a,b)	line_sfputc(__LINE__,a,b)
#undef	sfputr
#define sfputr(a,b,c)	line_sfputr(__LINE__,a,b,c)
#endif

#define SYNC()	do							\
		{							\
			if (line > prev && line > directive)		\
			{						\
				if (sfsprintf(buf, sizeof(buf), "\n#%s %d\n", sync <= 0 ? "line" : "", line + 1) <= line - prev)	\
				{					\
					sfputr(op, buf, -1);		\
					prev = line;			\
				}					\
				else while (prev < line)		\
				{					\
					prev++;				\
					sfputc(op, '\n');		\
				}					\
				data = 0;				\
			}						\
		} while (0)

#define DATA(c)	do							\
		{							\
			SYNC();						\
			data = 1;					\
			sfputc(op, c);					\
		} while (0)

/*
 * get next token on ip
 */

static char*
token(register Sfio_t* ip)
{
	register int	c;
	register char*	s;

	static char	buf[1024];

	s = buf;
	for (;;)
	{
		switch (c = sfgetc(ip))
		{
		case EOF:
			if (s > buf)
				break;
			return 0;
		case '\\':
		case '/':
		case '\n':
			sfungetc(ip, c);
			break;
		default:
			if (isspace(c))
			{
				if (s > buf)
				{
					sfungetc(ip, c);
					break;
				}
			}
			else if (s < &buf[sizeof(buf)-1])
				*s++ = c;
			continue;
		}
		break;
	}
	*s = 0;
	return buf;
}

/*
 * uncomment ip into op
 */

off_t
nocomment(register Sfio_t* ip, Sfio_t* op)
{
	register int	c = 0;
	register int	p;
	register int	data = 0;
	int		sync = 0;
	int		formals = 0;
	unsigned long	line = 0;
	unsigned long	prev = 0;
	unsigned long	directive = 0;
	Sfio_t*		pp = 0;
	off_t		count;
	int		quote;
	int		n;
	char*		s;
	char		buf[PATH_MAX];

	count = sftell(op);
	for (;;)
	{
	next:
		p = c;
		c = sfgetc(ip);
	check:
		switch (c)
		{
		case EOF:
			if (line != prev)
				sfputc(op, '\n');
			goto done;
		case 0:
			sfputc(op, c);
			sfmove(ip, op, SF_UNBOUND, -1);
			goto done;
		case '\\':
			DATA(c);
			switch (c = sfgetc(ip))
			{
			case EOF:
				goto done;
			case '\n':
				directive++;
				prev = ++line;
				sfputc(op, c);
				c = 0;
				break;
			default:
				sfputc(op, c);
				break;
			}
			break;
		case '\f':
		case '\t':
		case '\v':
			c = ' ';
			/*FALLTHROUGH*/
		case ' ':
			if (data) switch (p)
			{
			case ' ':
			case '\n':
			case '(':
			case '[':
			case ']':
			case '{':
			case '}':
			case ';':
			case ':':
			case '!':
			case '<':
			case '>':
			case '|':
			case ',':
			case '?':
			case '*':
				break;
			case ')':
				if (directive == line && formals)
				{
					formals = 0;
					sfputc(op, ' ');
				}
				break;
			default:
				switch (c = sfgetc(ip))
				{
				case '\n':
				case ')':
				case '[':
				case ']':
				case '{':
				case '}':
				case ';':
				case ':':
				case '=':
				case '!':
				case '<':
				case '>':
				case '|':
				case ',':
				case '?':
					break;
				case '(':
					if (directive == line && formals)
					{
						formals = 0;
						sfputc(op, ' ');
					}
					break;
				case '*':
					if (p == '/' || p == '=')
						sfputc(op, ' ');
					break;
				case '+':
				case '-':
				case '&':
					if (p == c || p == '=')
						sfputc(op, ' ');
					break;
				default:
					sfputc(op, ' ');
					break;
				}
				p = ' ';
				goto check;
			}
			break;
		case '\n':
			data = 0;
			line++;
			break;
		case '#':
			SYNC();
			directive = line;
			formals = 1;
			if (sync >= 0 && !data)
			{
				if (s = token(ip))
				{
					if (isdigit(*s))
					{
						sync = 1;
						directive = 0;
						line = strtol(s, NiL, 10);
						if (s = sfgetr(ip, '\n', 1))
						{
							while (isspace(*s))
								s++;
							if (*s == '"')
							{
								sfprintf(op, "# %d %s\n", line, s);
								prev = line;
							}
						}
						break;
					}
					if (!sync)
					{
						if (!pp && line <= 24 && streq(s, "pragma"))
						{
							if ((s = token(ip)) && streq(s, "prototyped"))
							{
								sync = -1;
								if (c = sffileno(ip))
								{
									n = dup(0);
									close(0);
									dup(c);
								}
								sfseek(ip, (Sfoff_t)(-1), SEEK_CUR);
								sfsync(ip);
								if (pp = sfpopen(NiL, "proto -fns", "r"))
									ip = pp;
								if (c)
								{
									close(0);
									dup(n);
								}
								c = 0;
								break;
							}
							DATA('#');
							sfputr(op, "pragma", ' ');
							sfputr(op, s, -1);
							c = 0;
							break;
						}
						if (!streq(s, "ident") && !streq(s, "line") && !streq(s, "pragma"))
							sync = -1;
					}
					DATA('#');
					sfputr(op, s, -1);
					c = 0;
				}
				else sync = -1;
			}
			if (c) DATA(c);
			else data = 1;
			break;
		case '/':
			switch (c = sfgetc(ip))
			{
			case EOF:
				goto done;
			case '/':
				for (;;) switch (p = c, c = sfgetc(ip))
				{
				case EOF:
					goto done;
				case '\n':
					if (p == '\\')
					{
						sfputc(op, p);
						sfputc(op, c);
						prev = ++line;
						break;
					}
					p = ' ';
					c = '\n';
					goto check;
				}
				break;
			case '*':
				for (;;) switch (p = c, c = sfgetc(ip))
				{
				case EOF:
					goto done;
				case '\n':
					directive++;
					line++;
					break;
				case '*':
					for (;;)
					{
						switch (c = sfgetc(ip))
						{
						case EOF:
							goto done;
						case '\n':
							directive++;
							line++;
							break;
						case '/':
							p = c = ' ';
							goto check;
						case '*':
							continue;
						}
						break;
					}
					break;
				}
				break;
			default:
				p = '/';
				DATA(p);
				goto check;
			}
			break;
		case '"':
		case '\'':
			DATA(c);
			quote = c;
			for (;;)
			{
				switch (c = sfgetc(ip))
				{
				case EOF:
					goto done;
				case '\\':
					sfputc(op, c);
					switch (c = sfgetc(ip))
					{
					case EOF:
						goto done;
					case '\n':
						directive++;
						prev = ++line;
						break;
					}
					break;
				case '"':
				case '\'':
					if (c == quote)
					{
						sfputc(op, c);
						goto next;
					}
					break;
				case '\n':
					p = ' ';
					goto check;
				}
				sfputc(op, c);
			}
			break;
		default:
			DATA(c);
			break;
		}
	}
 done:
	count = count < 0 ? 0 : (sferror(ip) || sferror(op)) ? -1 : (sftell(op) - count);
	if (pp) sfclose(pp);
	return count;
}
