#include "wuj.h"
#include <malloc.h>
#include <string.h>
#include <ctype.h>

#define LINE_LEN 	2000

#define T_C_BGROUP	0x0001
#define T_BDMATH	0x0002
#define T_BMATH		0x0004
#define T_BENV		0x0008

Local Logical restore_struct(Byte type, char *name);
Local void error(char *, char *);

struct entry
   {
    int  line;
    Byte type;
    char *env_name;
   }*block;

#define BLOCK_SIZE 	((int)(0x8000U / sizeof(struct entry)))
#define BUF_SIZE	32767

Local int  block_pos = 0;
Local int  line_count = 0;

Local Logical b_name_flag = False;
Local Logical e_name_flag = False;
Local Logical math_flag = False;
Local Logical dmath_flag = False;

main(int argc, char *argv[])

{
    char *inp_file;
    char *buffer;
    FILE *inp;
    char *inp_buff;

    fprintf(stderr, "TeXchk 23.08.1992 by W. Macewicz\n");
    if(argc < 2)
        inp = stdin;
     else
       {
	if(argv[1][0] == '-')
	    error("Usage:\n\ttexchk [<source file>[.tex]]", EMPTY_STRING);
	if(strchr(argv[1], '.') == NULL)
	   {
	    inp_file = (char *)malloc(strlen(argv[1]) + 5);
	    if(inp_file == NULL)
		error("Can't allocate memory", EMPTY_STRING);
	    strcpy(inp_file, argv[1]);
	    strcat(inp_file, ".tex");
	   }
	 else if(argv[1][strlen(argv[1]) - 1] == '.')
	   {
	    inp_file = argv[1];
	    argv[1][strlen(argv[1]) - 1] = '\0';
	   }
	 else
	    inp_file = argv[1];
	if((inp = fopen(inp_file, "r")) == NULL)
	    error("Can't open %s", inp_file);
        }
    if((inp_buff = (char *)malloc(BUF_SIZE)) == NULL)
        error("Can't allocate memory", EMPTY_STRING);
    setvbuf(inp, inp_buff, _IOLBF, BUF_SIZE);
    block = (struct entry *)calloc(BLOCK_SIZE, sizeof(struct entry));
    if(block == NULL)
        error("Can't allocate memory", EMPTY_STRING);
    buffer = (char *)malloc(LINE_LEN + 1);
    if(buffer == NULL)
        error("Can't allocate memory", EMPTY_STRING);
    while(fgets(buffer, LINE_LEN, inp) != NULL)
       {
        int n;

	line_count++;
	n = strlen(buffer);
	for(int i = 0; i < n; i++)
	   {
	    if((b_name_flag || e_name_flag) && isalpha(buffer[i]))
	       {
	        char *nn,
		     *m;
		register int j;

		nn = &buffer[i];
		for(j = 0; isalpha(buffer[i]); i++, j++)
		   ;
		m = (char *)malloc(j + 1);
		if(m == NULL)
		    error("Can't allocate memory", EMPTY_STRING);
		strncpy(m, nn, j);
		m[j] = '\0';
		if(buffer[i] != '}')
		    printf("Brak } po \\begin{%s w linii %d\n", m, line_count);
		 else
		    i++;
	        if(b_name_flag)
		   {
		    block[block_pos].type = T_BENV;
		    block[block_pos].line = line_count;
		    block[block_pos++].env_name = m;
		    b_name_flag = False;
		   }
	        if(e_name_flag)
		   {
		    if(block_pos == 0)
			printf("\\end{%s} w linii %d\n", m, line_count);
		     else if(block[block_pos - 1].type != T_BENV)
		       {
			if(restore_struct(T_BENV, m))
			    printf("\\end{%s} w linii %d\n", m, line_count);
		       }    
		     else if(strcmp(block[block_pos - 1].env_name, m))
		       {
			if(restore_struct(T_BENV, m))
			    printf("\\end{%s} w linii %d\n", m, line_count);
		       }    
		     else
		       {
			block_pos--;
			free(block[block_pos].env_name);
		       }
		    free(m);
		    e_name_flag = False;
		   }
	       }
	    switch(buffer[i])
	       {
		case '\\':
		    if(isalpha(buffer[i + 1]))
		       {
			if(strncmp("begin", &buffer[i + 1], 5) == 0)
			   {
			    if(isalpha(buffer[i + 6]))
			       {
			        i++;
				break;
			       }
			    b_name_flag = True;
			    i += 6;
			   }
			 else if(strncmp("end", &buffer[i + 1], 3) == 0)
			   {
			    if(isalpha(buffer[i + 4]))
			       {
			        i++;
				break;
			       }
			    e_name_flag = True;
			    i += 4;
			   }
			 else
			    i++;
		       }
		     else
			i++;
		    break;
		case '%':
		    goto next_line;
		case '$':
		    if(buffer[i + 1] == '$')
		       {
			i++;
			if(!dmath_flag)
			   {
			    dmath_flag = True;
			    if(block_pos >= BLOCK_SIZE)
				error("Block buffer overfull", EMPTY_STRING);
			    block[block_pos].type = T_BDMATH;
			    block[block_pos++].line = line_count;
			   }
			 else
			   {
			    if(block[block_pos - 1].type != T_BDMATH)
				(void)restore_struct(T_BDMATH, "$$");
			     else
				block_pos--;
			    dmath_flag = False;
			   }
		       }
		     else
		       {
			if(!math_flag)
			   {
			    math_flag = True;
			    if(block_pos >= BLOCK_SIZE)
				error("Block buffer overfull", EMPTY_STRING);
			    block[block_pos].type = T_BMATH;
			    block[block_pos++].line = line_count;
			   }
			 else
			   {
			    if(block[block_pos - 1].type != T_BMATH)
				(void)restore_struct(T_BMATH, "$");
			     else
				block_pos--;
			    math_flag = False;
			   }
		       }
		    break;
		case '{':
		    if(b_name_flag || e_name_flag)
		        break;
		    if(block_pos >= BLOCK_SIZE)
			error("Block buffer overfull", EMPTY_STRING);
		    block[block_pos].type = T_C_BGROUP;
		    block[block_pos++].line = line_count;
		    break;
		case '}':
		    if(block_pos == 0)
			printf("Zbędny } w linii %d\n", line_count);
		     else if(block[block_pos - 1].type != T_C_BGROUP)
		       {
			if(restore_struct(T_C_BGROUP, "}"))
			    printf("Zbędny } w linii %d\n", line_count);
		       }    
		     else
			block_pos--;
		    break;
	       }
	   }
next_line: ;
       }
    if(block_pos > 0)
       {
        register int i;

	printf("Niezamknięte struktury:\n");
	for(i = 0; i < block_pos; i++)
	   {
	    switch(block[i].type)
	       {
		case T_BENV:
		    printf("\\begin{%s}\t(%d)\n", block[i].env_name, 
		    						block[i].line);
		    break;
		case T_C_BGROUP:
		    printf("{ \t(%d)\n", block[i].line);
		    break;
		case T_BMATH:
		    printf("$ \t(%d)\n", block[i].line);
		    break;
		case T_BDMATH:
		    printf("$$ \t(%d)\n", block[i].line);
		    break;
	       }
	   }
       }
    return(0);
}

Local Logical 
restore_struct(Byte e_type, char *name)

{
    for(int j = block_pos - 1; j >= 0; j--)
	if(block[j].type == e_type)
	   {
	    if(e_type == T_BENV)
	       {
	        if(strcmp(block[j].env_name, name))
		   continue;
	       }
	    for( ; j < (block_pos - 1); j++)
		block[j] = block[j + 1];
	    block_pos--;
	    if(e_type == T_BENV)
		printf("\\end{%s} w linii %d skrzyżowany z ", name, line_count);
	     else
		printf("%s w linii %d skrzyżowany z ", name, line_count);
	    switch(block[block_pos - 1].type)
	       {
		case T_BENV:
		    printf("\\begin{%s} ", block[block_pos - 1].env_name);
		    break;
		case T_C_BGROUP:
		    printf("{ ");
		    break;
		case T_BMATH:
		    printf("$ ");
		    break;
		case T_BDMATH:
		    printf("$$ ");
		    break;
	       }
	    printf("w linii %d\n", block[block_pos - 1].line);
	    return(False);
	   }
    return(True);
}

Local void 
error(char *fmt, char *str)
{
    fprintf(stderr, fmt, str);
    exit(1);
}
