

#include	<errno.h>
#include	<stdio.h>
#include	<fcntl.h>
#include	<stdlib.h>
#include	"p2c.h"

int fn_code();
int fn_document();
int fn_line_header();
int fn_line_terminal();
int fn_include();
int fn_esc();
int fn_include();
int fn_binary();

ENV env_template = {
	0,
	"",
	{
		{"@esc",fn_esc},
		{"@code",fn_code},
		{"@document",fn_document},
		{"@line-header",fn_line_header},
		{"@line-terminal",fn_line_terminal},
		{"@include",fn_include},
		{"@binary",fn_binary},
		{""}
	},
	"@&$/",
	"printf(",
	");"
};

int mode;
#define M_CODE		0
#define M_DOCUMENT	1
#define M_DOC_CODE	2
ENV * env_stack;

fn_esc(ENV * env,FILE * out,char * buf)
{
int len;
	len = strlen(buf);
	if ( len > 4 )
		memcpy(env->char_esc,buf,4);
	else	memcpy(env->char_esc,buf,len);
}

fn_code(ENV * env,FILE * out,char * buf)
{
	if ( buf[0] )
		output_code(env,out,buf);
	else	mode = M_CODE;
}

fn_document(ENV * env,FILE * out,char * buf)
{
	mode = M_DOCUMENT;
}

fn_line_header(ENV * env,FILE * out,char * buf)
{
	strcpy(env->line_header,buf);
}

fn_line_terminal(ENV * env,FILE * out,char * buf)
{
	strcpy(env->line_terminal,buf);
}


output_code(ENV * env,FILE * out,char * buf)
{
int len;
	len = strlen(buf);
	fprintf(out,"%s\n",buf);
}

output_doc_code(ENV * env,FILE * out,char * buf)
{
int len;
	len = strlen(buf);
	if ( len && buf[len-1] == env->char_esc[E_CR] ) {
		buf[len-1] = 0;
	}
	else	mode = M_DOCUMENT;
	fprintf(out,"%s",buf);
	if ( mode == M_DOCUMENT )
		fprintf(out,"%s\n",env->line_terminal);
	else	fprintf(out,"\n");
}

output_document(ENV * env,FILE * out,char * buf)
{
int len;
int i;

	fprintf(out,"\t%s\"",env->line_header);
	for ( i = 0 ; ; i ++ ) {
		if ( buf[i] == env->char_esc[E_CHAR] ) {
			i ++;
			if ( buf[i] == 0 )
				goto err2;
			fprintf(out,"%c",buf[i]);
			continue;
		}
		else if ( buf[i] == env->char_esc[E_AFTER] ) {
			fprintf(out,"\\n\"");
			i ++;
			goto after;
		}
		switch ( buf[i] ) {
		case '%':
			fprintf(out,"%%%%");
			break;
		case '\t':
			fprintf(out,"\\t");
			break;
		case '\\':
			fprintf(out,"\\\\");
			break;
		case '"':
			fprintf(out,"\\\"");
			break;
		case 0:
			fprintf(out,"\\n\"");
			goto end;
		default:
			fprintf(out,"%c",buf[i]);
			break;
		}
	}

after:
	len = strlen(&buf[i]);
	if ( len && buf[i+len-1] == env->char_esc[E_CR] ) {
		buf[i+len-1] = 0;
		mode = M_DOC_CODE;
	}
	fprintf(out,"%s",&buf[i]);
end:
	if ( mode == M_DOCUMENT ) {
		fprintf(out,"%s",env->line_terminal);
	}
	fprintf(out,"\n");
	return 0;
err1:
	perror("output error\n");
	exit(1);
err2:
	fprintf(stderr,"output error ESC end\n");
	exit(1);
}

fn_include(ENV * env,FILE * out,char * buf)
{
ENV new;
int i;
int in;
	new = *env;
	if ( buf[0] == '/' )
		strcpy(new.path,buf);
	else {
		for ( i = strlen(new.path)-1 ;
			i > 0 && new.path[i] != '/' ; i -- );
		if ( new.path[i] == '/' )
			i ++;
		strcpy(&new.path[i],buf);
	}
	in = open(new.path,O_RDONLY);
	if ( in <  0 ) {
		perror("read error 10");
		exit(1);
	}
	exe(&new,in,out);
	close(in);
}

char *
get_line(int fd)
{
char * ret;
int i;
char ch;
	ret = malloc(1);
	ret[0] = 0;
	i = 0;
	for ( ; ; ) {
		errno = 0;
		if ( read(fd,&ret[i],1) < 1 ) {
			if ( errno == 0 ) {
				if ( i == 0 )
					return 0;
				ret[i] = 0;
				return ret;
			}
			perror("read error in header");
			exit(1);
		}
		switch ( ret[i] ) {
		case '\n':
		case '\r':
			break;
		default:
			i ++;
			ret = realloc(ret,i+1);
			continue;
		}
		break;
	}
	ret[i] = 0;
	return ret;
}


exe(ENV * env,int in,FILE * out)
{
char * buf;
int i;
char * ptr;
int len;
	for ( ; ; ) {
		buf = get_line(in);
		if ( buf == 0 ) {
			return;
		}
		if ( buf[0] == env->char_esc[E_LINE] ) {
			for ( i = 0 ; env->command[i].name[0] ; i ++ ) {
				len = strlen(env->command[i].name);
				if ( memcmp(env->command[i].name,
						buf,len) == 0 &&
					(buf[len] == ' ' ||
						buf[len] == '\t' ||
						buf[len] == 0 ) ) {
					for ( ptr = &buf[len] ; 
						*ptr == '\t' ||
						*ptr == ' ' ; ptr ++);
					(*env->command[i].func)(
						env,out,ptr);
				}
			}
		}
		else if ( mode == M_CODE )
			output_code(env,out,buf);
		else if ( mode == M_DOC_CODE )
			output_doc_code(env,out,buf);
		else	output_document(env,out,buf);
		free(buf);
	}
}

main(int argc,char ** argv)
{
char ch;
int in;
FILE * out;
char * buf;
int i,len;
char * ptr;
ENV env;
	if ( argc < 3 ) {
		fprintf(stderr,"too few argments\n");
		exit(1);
	}
	in = open(argv[1],O_RDONLY);
	if ( in < 0 ) {
		perror("input file");
		exit(1);
	}
	out = fopen(argv[2],"w+");
	if ( out == 0 ) {
		perror("output file");
		exit(1);
	}
	env = env_template;
	strcpy(env.path,argv[1]);
	exe(&env,in,out);
	close(in);
	fclose(out);
	exit(0);
}
