#!/usr/bin/perl -W
#
# konw-fasm.pl - Skrypt konwertujacy pliki naglowkowe jezyka C na pliki naglowkowe dla FASMa
# konw-fasm.pl - A script which converts C header files to FASM-compatible header files
#
#	Copyright (C) 2006-2007 Bogdan 'bogdro' Drozdowski, http://rudy.mif.pg.gda.pl/~bogdro/inne/
#		(bogdandr AT op.pl, bogdro AT rudy.mif.pg.gda.pl)
#
#	Licencja / Licence:
#	Powszechna Licencja Publiczna GNU v2 / GNU General Public Licence v2
#
#	Ostatnia modyfikacja / Last modified : 2007-01-17
#
#	Sposob uzycia / Syntax:
#		./konw-fasm.pl xxx.h [yyy.inc]
#
# Jesli nazwa pliku wyjsciowego nie zostanie podana, jest brana taka sama jak pliku
#	wejsciowego (tylko rozszerzenie sie zmieni na .inc). Jesli za nazwe
#	pliku wejsciowego podano "-", czytane jest standardowe wejscie.
#	Jesli nazwa pliku wyjsciowego to "-" (lub nie ma jej, gdy wejscie to stdin),
#	wynik bedzie na standardowym wyjsciu.
#
# If there's no output filename, then it is assumed to be the same as the
#	input filename (only the extension will be changed to .inc). If the
#	input filename is "-", standard input will be read. If the
#	output filename is "-" (or missing, when input is stdin),
#	the result will be written to the standard output.
#
#    Niniejszy program jest wolnym oprogramowaniem; mozesz go
#    rozprowadzac dalej i/lub modyfikowac na warunkach Powszechnej
#    Licencji Publicznej GNU, wydanej przez Fundacje Wolnego
#    Oprogramowania - wedlug wersji 2-giej tej Licencji lub ktorejs
#    z pozniejszych wersji.
#
#    Niniejszy program rozpowszechniany jest z nadzieja, iz bedzie on
#    uzyteczny - jednak BEZ JAKIEJKOLWIEK GWARANCJI, nawet domyslnej
#    gwarancji PRZYDATNOSCI HANDLOWEJ albo PRZYDATNOSCI DO OKRESLONYCH
#    ZASTOSOWAN. W celu uzyskania blizszych informacji - Powszechna
#    Licencja Publiczna GNU.
#
#    Z pewnoscia wraz z niniejszym programem otrzymales tez egzemplarz
#    Powszechnej Licencji Publicznej GNU (GNU General Public License);
#    jesli nie - napisz do Free Software Foudation:
#		Free Software Foundation
#		51 Franklin Street, Fifth Floor
#		Boston, MA 02110-1301
#		USA
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foudation:
#		Free Software Foundation
#		51 Franklin Street, Fifth Floor
#		Boston, MA 02110-1301
#		USA

use strict;
use warnings;
use Getopt::Long;

my ($wyj);

my ($help, $lic, $help_msg, $lic_msg);

$help_msg = "$0:\n Skrypt konwertujacy pliki naglowkowe jezyka C na pliki naglowkowe dla FASMa\n".
	"  / A script which converts C header files to FASM-compatible header files.\nAutor/Author: Bogdan Drozdowski, ".
	"http://rudy.mif.pg.gda.pl/~bogdro/inne/\n".
	"Skladnia/Syntax: $0 [--help] [--license] xxx.h [yyy.inc]\n\n
 Jesli nazwa pliku wyjsciowego nie zostanie podana, jest brana taka sama jak
pliku wejsciowego (tylko rozszerzenie sie zmieni na .inc). Jesli za nazwe
pliku wejsciowego podano \"-\", czytane jest standardowe wejscie.
 Jesli nazwa pliku wyjsciowego to \"-\" (lub nie ma jej, gdy wejscie to stdin),
wynik bedzie na standardowym wyjsciu.

 If there's no output filename, then it is assumed to be the same as the
input filename (only the extension will be changed to .inc). If the
input filename is \"-\", standard input will be read.
 If the output filename is \"-\" (or missing, when input is stdin),
the result will be written to the standard output.\n";

$lic_msg = "$0:\n Skrypt konwertujacy pliki naglowkowe jezyka C na pliki naglowkowe dla FASMa\n".
	"  / A script which converts C header files to FASM-compatible header files.\nAutor/Author: Bogdan Drozdowski, ".
	"http://rudy.mif.pg.gda.pl/~bogdro/inne/\n\n".
	"    Niniejszy program jest wolnym oprogramowaniem; mozesz go
    rozprowadzac dalej i/lub modyfikowac na warunkach Powszechnej
    Licencji Publicznej GNU, wydanej przez Fundacje Wolnego
    Oprogramowania - wedlug wersji 2-giej tej Licencji lub ktorejs
    z pozniejszych wersji.

    Niniejszy program rozpowszechniany jest z nadzieja, iz bedzie on
    uzyteczny - jednak BEZ JAKIEJKOLWIEK GWARANCJI, nawet domyslnej
    gwarancji PRZYDATNOSCI HANDLOWEJ albo PRZYDATNOSCI DO OKRESLONYCH
    ZASTOSOWAN. W celu uzyskania blizszych informacji - Powszechna
    Licencja Publiczna GNU.

    Z pewnoscia wraz z niniejszym programem otrzymales tez egzemplarz
    Powszechnej Licencji Publicznej GNU (GNU General Public License);
    jesli nie - napisz do Free Software Foudation:
		Free Software Foundation
		51 Franklin Street, Fifth Floor
		Boston, MA 02110-1301
		USA

 This program is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License
 as published by the Free Software Foundation; either version 2
 of the License, or (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software Foudation:
		Free Software Foundation
		51 Franklin Street, Fifth Floor
		Boston, MA 02110-1301
		USA\n";

if ( @ARGV == 0 ) {
	print $help_msg;
	exit 1;
}

Getopt::Long::Configure ( "ignore_case", "ignore_case_always" );

if ( ! GetOptions(
	'h|help|?'		=>	\$help,
	'license|licence|L'	=>	\$lic
	)
   ) {

	print $help_msg;
	exit 1;
}

if ( $lic ) {
	print $lic_msg;
	exit 1;
}

if ( @ARGV == 0 || $help ) {
	print $help_msg;
	exit 1;
}

if ( @ARGV > 1 && $ARGV[0] ne "-" && $ARGV[0] eq $ARGV[1] ) {

	print "$0: Plik wejsciowy i wyjsciowy NIE moga byc takie same.\n";
	print "$0: Input file and output file must NOT be the same.\n";
	exit 4;
}


##########################################################
# Otwieranie plikow (opening the files)

if ( !open ( WE, $ARGV[0] ) ) {
#	$! jest trescia bledu. ($! is the error message)
	print "$0: $ARGV[0]: $!\n";
	exit 2;
}

if ( @ARGV > 1 )  {
	$wyj = $ARGV[1];

} else {
	# bierzemy tylko nazwe pliku (take only the filename)
	($wyj = $ARGV[0]) =~ s/.*\/([^\/]+)/$1/;
# 	Zmieniamy rozszerzenie z .h na .inc (change the extension from .h to .inc)
	$wyj =~ s/\.h$/\.inc/;
#	Zmieniamy spacje na podkreslenia (change spaces to underlines)
	$wyj =~ s/\s+/_/g;

	if ( $wyj eq $ARGV[0] && $wyj ne "-" ) { $wyj .= ".inc"; }
}

if ( !open ( WY, "> $wyj" ) ) {
	print "$0: $wyj: $!\n";
	close WE;
	exit 3;
}

##########################################################
# Przetwarzanie (processing):

CZYTAJ: while ( <WE> ) {

	#	puste linie przepisujemy (empty lines go without change)
	if (/^\s*$/) {
		print WY "\n";
		next;
	}

	# sklejanie wierszy (joining lines)
	while ( /\\$/ ) {

		s/\\\n//;
		$_ .= <WE>;
	}

	# sprawdzamy, czy komentarz jest jedyny na linii (check if a comment is the only thing on this line)
	if ( /^\s*\/\*.*\*\/\s*$/ ) { &komen(1); next; }

	# przetwarzanie dyrektyw kompilacji warunkowej (processing of conditional compiling directives)
	&kom_war(1);


	#	Include'y (includes)
	if ( /^\s*#\s*include/ ) {

		s/\s*#\s*include\s*[<\"]([\w\/\.]+)[>\"](.*)$/include\t"$1" $2 \n/;
		#	nazwa pliku	  ^^^^^^ 	(filename)

		print WY;
		next;
	}

	#	Extern'y (externs)

	if ( /^\s*extern/ ) {

		&komen(0);
		#	extern "C" {
		if ( /\s*extern\s*\"C\"\s*{/ ) { s/\s*extern\s*\"C\"\s*{//; print WY; next; }

		# laczymy linie (joining lines)
		while ( ! /;/ ) { s/\n//; $_ .= <WE>; }

		#	extern'y funkcji (external functions)

		# najpier usuwamy (first remove): extern MACRO_NAME ( fcn name, args, ... )
		s/\s*\w*\s*extern[\w\*\s]+\s+[A-Z0-9_\*]+\s*\(.*//;
		# 	typ	  ^^^^^^	(type)

		# uwaga na rozne makra przed argumentami (look out for various macroes in front of the arguments)
		# poczatkowe \w* jest dla __attribute__ itp. (the leading \w* is for __attribute__ etc.)
		s/\s*\w*\s*extern[\w\*\s]+[A-Z0-9_\*]*\s+[\(\*\s]*([a-z0-9_]+)\s*\)*\s*[A-Z0-9_\*]*\s*\(.*/extrn\t$1\n/;
		# 	typ	  ^^^^^^	nazwa funkcji	    ^^^^^^^	makra   ^^^^^^^  arg    ^^ (type, name, macroes, arg)

		#	extern'y zmiennych (external variables)
		s/\s*extern[\w\*\s]+\s+\**(\w+)\s*;/extrn\t$1\n/;
		#	typ ^^^^^^   nazwa ^^^	(type, name)

		print WY;
		next;
	}

	#	typedef

	if ( /typedef/ ) {

		if ( /\(/ ) { while ( ! /\)/ or /,\s*$/ ) { s/\n//; $_ .= <WE>; } }

		if ( /\(/ ) { s/^.*$/\n/; }
		elsif ( !/{/ && /;/ )	{ s/\s*typedef([\w\*\s]+)\s+\**(\w+)\s*;/$2\tequ\t$1/; print WY; next CZYTAJ; }
		#			stary typ	^^^^^^ nowy^^^	(old type, new)
		# "typedef struct ...."  ----> "struct ....."
		elsif ( /(struct|union|enum)/ )	{ s/\s*typedef\s+//; }

		# tu nie ma NEXT (no NEXT here).

	}

	#	Struktury (structures):
	# Pomijamy wyrazenia typu: (skip over expressions of the type):
	# struct xxx;
	# struct xxx function(arg1, ...);

	if ( /^\s*struct/ && ! /^\s*struct[^\{]+;.*$/i && !/\(/ ) {

		# pomijamy (skip over) "struct foo;"
		if ( /^\s*struct\s*\w+\s*;/ ) { next; }

		# nazwa struktury (the name of the structure)
		s/\s*struct\s+(\w+)/struc\t$1 /;
		my $nazwa;
		if ( $1 and $1 ne "" and $1 !~ /\{/ ) { $nazwa = $1; }
		else { $nazwa = ""; }

		# jesli byl '{' w pierwszej linii, dajemy go do drugiej
		# (if there was a '{' in the first line, we put it in the second)
		s/\s*\{/\n\{\n/;

		# struktura moze sie konczyc w tej samej linijce lub zawierac wiele deklaracji na linie
		#   po prostu stawiamy znak nowej linii za kazdym srednikiem i idziemy dalej
		# (a structure can end in the same line or contain many declaration per line
		#   we simply put a newline after each semicolon and go on)

		s/;/;\n/g;
		# zmiana komentarzy (changing the comments)
		&komen(1);

		do {
			# sklejanie wierszy (joining lines)
			while ( /\\$/ ) {

				s/\\\n//;
				$_ .= <WE>;
			}

			# wiele zmiennych tego samego typu - dajemy kazda do osobnej linii wraz z typem
			# (many variables of the same type - we put each on a separate line together with its type)
			if ( /\/\*/ ) {

				while ( /,\s*$/ ) { s/\n//; $_ .= <WE>; }
				while ( /,.*\/\*/ && !/\(/ ) {

					if ( /\[.*\/\*/ ) { s/([\w ]*)\s+(\w+)\s*(\[\w+\]),\s*(.*)/$1 $2$3;\n$1 $4/; }
					else { s/([\w ]*)\s+([\w\*]+)\s*,\s*(.*)/$1 $2;\n$1 $3/; }
					#       typ^^^ nazwa  ^^^^      ^    ^^	(type, name)
                                }

			} else {

				while ( /,\s*$/ ) { s/\n//; $_ .= <WE>; }
				while ( /,.*/ && !/\(/ ) {

					if ( /\[.*/ ) { s/([\w ]*)\s+(\w+)\s*(\[\w+\]),\s*(.*)/$1 $2$3;\n$1 $4/; }
					else { s/([\w ]*)\s+([\w\*]+)\s*,\s*(.*)/$1 $2;\n$1 $3/; }
					#       typ^^^ nazwa  ^^^^      ^    ^^	(type, name)
                                }
			}

			# zmiana komentarzy (changing the comments)
			&komen(0);

			if ( /^\s*union\s+(\w+)/ ) {

				my $roz = &zrob_unie(0);
				print WY "\t.$1\trb $roz\n" if $roz;
				goto STR_END;
			}

			if ( /^\s*union/ ) {

				my $roz = &zrob_unie(1);
				#print WY "\t\trb $roz\n" if $roz;
				goto STR_END;
			}

			# najpierw pozbywamy sie ":cyfra" w polach struktur
			# (first we remove the ":digit" from the structure fields)
			s/(.*):\s*\d+\s*/$1/g;

			# pomijamy (skip over) 'volatile'
			s/_*volatile_*//g;

			# wskazniki do funkcji (pointers to functions)
			s/.+\(\s*\*\s*(\w+)\s*\)\s*\(.*\)\s*;/\t\.$1:\t\trd 1/g;
			# typ wskaznikowy (pointer type)
			s/.+\*\s*(\w+)\s*;/\t\.$1:\t\trd 1/g;

			# tablice (arrays)
			s/.*struct\s+(\w+)\s+(\w+)\s*\[(\w+)\]\s*;/\t\.$2:\t\t$1/g;
			s/.*(signed|unsigned)?\s+long\s+long(\s+int)?\s+(\w+)\s*\[(\w+)\]\s*;/\t\.$3:\t\trq $4/g;
			s/.*long\s+double\s+(\w+)\s*\[(\w+)\]\s*;/\t\.$1:\t\trt $2/g;
			s/.*(char|unsigned\s+char|signed\s+char)\s+(\w+)\s*\[(\w+)\]\s*;/\t\.$2:\t\trb $3/g;
			s/.*float\s+(\w+)\s*\[(\w+)\]\s*;/\t\.$1:\t\trd $2/g;
			s/.*double\s+(\w+)\s*\[(\w+)\]\s*;/\t\.$1:\t\trq $2/g;
			s/.*(short|signed\s+short|unsigned\s+short){1}(\s+int)?\s+(\w+)\s*\[(\w+)\]\s*;/\t\.$3:\t\trw $4/g;
			s/.*(long|signed|signed\s+long|unsigned|unsigned\s+long|int){1}(\s+int)?\s+(\w+)\s*\[(\w+)\]\s*;/\t\.$3:\t\trd $4/g;

			# typy zmiennych (variables' types)
			# wszystkie "\w+" oznaczaja nazwe zmiennej (all "\w+" stand for the variable name)
			s/.*struct\s+(\w+)\s+(\w+)\s*;/\t\.$2:\t\t$1/g;
			s/.*(signed|unsigned)?\s+long\s+long(\s+int)?\s+(\w+)\s*;/\t\.$3:\t\trq 1/g;
			s/.*long\s+double\s+(\w+)\s*;/\t\.$1:\t\trt 1/g;
			s/.*(char|unsigned\s+char|signed\s+char)\s+(\w+)\s*;/\t\.$2:\t\trb 1/g;
			s/.*float\s+(\w+)\s*;/\t\.$1:\t\trd 1/g;
			s/.*double\s+(\w+)\s*;/\t\.$1:\t\trq 1/g;
			s/.*(short|signed\s+short|unsigned\s+short){1}(\s+int)?\s+(\w+)\s*;/\t\.$3:\t\trw 1/g;
			s/.*(long|signed|signed\s+long|unsigned|unsigned\s+long|int){1}(\s+int)?\s+(\w+)\s*;/\t\.$3:\t\trd 1/g;

			# szukamy konca struktury (look for the end of the structure)
			if ( /\s*\}.*/ ) {

				# dolaczamy definicje rozmiaru struktury (add a structure size definition)
				if ( $nazwa ne "" && $nazwa !~/\{/ )
					{ s/\s*\}.*/\n\t${nazwa}_size\t=\t\$ - .\n\}\n/; }
				else { s/\s*\}.*/\n\}\n/; }
				print WY;
				next CZYTAJ;
			}

			# przetwarzanie dyrektyw kompilacji warunkowej (processing of conditional compiling directives)
			&kom_war(0);
			print WY;

	STR_END: } while ( <WE> );

		next CZYTAJ;
	}

	#	enum

	if ( /^\s*enum/ ) {

		# usuwamy slowo enum i jego nazwe (remove the 'enum' and its name)
 		s/.*enum\s+\w+\s*\{?//;

		my $wartosc = 0;

		# sprawdzamy 1-linijkowe (check if one-line enum)
		if ( /}/ ) {

			# zmiana komentarzy (changing the comments)
			&komen(1);
			# nie ma dyrektyw kompilacji warunkowej (there are no conditional compiling directives)
			#&kom_war(0);


			while ( /,.*;/ ) {

				s/([\w\s]*)\s+(\w+)\s*=\s*(\w+)\s*,/$1\n$2\t=\t$3\n/ and $wartosc = $3+1;
				#   ^^^^       ^^^         ^^^	cokolwiek, nazwa, wartosc (anything, name, value)
				s/([\w\s]*)\s+(\w+)\s*,/$1\n$2\t=\t$wartosc\n/ and $wartosc++;

			}

			# ostatnia linia nie ma przecinka (the last line has no comma)
			s/([\w\s]*)\s+(\w+)\s*=\s*(\w+)\s*\}\s*;/$1\n$2\t=\t$3\n/;
			s/([\w\s]*)\s+(\w+)\s*\}\s*;/$1\n$2\t=\t$wartosc\n/;

			print WY;
			next CZYTAJ;

		}

		while ( <WE> ) {

			# zmiana komentarzy (changing the comments)
			&komen(1);

			# przetwarzanie dyrektyw kompilacji warunkowej (processing of conditional compiling directives)
			if ( /^\s*#/ ) { &kom_war(1); next; }

			# pomiajmy pierwszy znak '{' (skip over the first '{' character)
			if ( /^\s*\{\s*$/ ) { next; }

			# jesli stala ma przypisana wartosc, nie ruszamy jej
			# (if the constant has a value, we don't touch it)
			if ( ! /=/ ) {

				s/\s*(\w+)\s*,?/$1\t=\t$wartosc\n/;
				$wartosc++;

			} else {

				# przypisujemy kolejna wartosc
				# (assign a subsequent value)
				s/\s*(\w+)\s*=\s*(\w+)\s*,?/$1\t=\t$2\n/;
				$wartosc = $2 + 1;
			}

			# szukamy konca enum (look for the end of the enum)
			if ( /\s*\}.*/ ) {
				next CZYTAJ;
			}

			print WY;

		};

	}

	if ( /union/ ) {

		&zrob_unie(1);
		next;
	}

	# zmiana komentarzy (change the comments)
	&komen(1);

}	# koniec petli CZYTAJ (end of CZYTAJ loop)


##########################################################
# Zmiana komentarzy (changing the comments):

sub	komen {

	# parametr = 0 => nie ma drukowania (when the parameter is 0, there will be no printing)
	my $drukuj = shift;

	#	Komentarze jednolinijkowe (one-line comments):
	if ( /(.+)\/\*(.*)\*\/(.+)$/ )	{ s/(.+)\/\*(.*)\*\/(.+)$/$1 $3 ; $2/g;	return $_; }
	if ( /\/\*(.*)\*\/(.+)$/ )	{ s/\/\*(.*)\*\/(.+)$/$2 ; $1/g;	return $_; }
	if ( /(.+)\/\*(.*)\*\/$/ )	{ s/(.+)\/\*(.*)\*\/$/$1 ; $2/g;	return $_; }

	# Jesli komentarz jest jedyna rzecza w wierszu, to od razu go wypisujemy i idziemy dalej
	# (if the comment is the only thing on this line, we print it to the file and go further)
	if ( /^\/\*(.*)\*\/$/ )		{ s/\/\*(.*)\*\/$/; $1/g; print WY if $drukuj;	return $_; }

	# Komentarze wielolinijkowe (multi-line comments)
	if ( /(.*)\/\*(.*)$/ ) {

		# wyrzucamy /* z zachowaniem tego, co bylo przed tym
		# (we remove the /*, saving whatever was in front of it)
		s/(.*)\/\*(.*)$/$1; $2/;
		print WY if $drukuj;

		while ( <WE> ) {

			# po prostu stawiamy srednik przed kazda linia
			# (we simply add a semicolon in front of every line)
			s/^(.*)$/; $1/;

			# jak trafimy na */, to go kasujemy i przerywamy ta petle
			# (when we find */, we remove it and stop this loop)
			if ( /\*\// ) {

				s/\*\///;
				print WY if $drukuj;
				$_ ="";
				return;# $_;
			}
			print WY if $drukuj;
		}
	}

}

##########################################################
# Kompilacja warunkowa (conditional compiling)

sub	kom_war {

	# parametr = 0 => nie ma drukowania (when the parameter is 0, there will be no printing)
	my $drukuj = shift;

	# zmiana komentarzy (changing the comments)
	&komen($drukuj);

	# sklejanie wierszy (joining lines)
	while ( /\\$/ ) {

		s/\\\n//;
		$_ .= <WE>;
	}

	# pomijamy makra (skip over macros)
	if ( /^\s*\#\s*define\s+\w+\s*\(/ )	{ s/\s*\#\s*define\s+\w+\s*\(.*/\n/; return; }


	#	Definicje stalych #define (definitions of #define constants)
	if ( /^\s*#\s*define\s+/ ) {

		# bez wartosci - ustawiamy na 1; zasieg: assembler (without a value - set to 1; scope: assembler)
		s/\s*#\s*define\s+(\w+)\s*$/$1\t=\t1\n/;

		# z wartoscia; zasieg: assembler (with a value; scope: assembler)
		# dopuszczamy "(-cyfry)" / "(-digits)" is allowed
		s/\s*#\s*define\s+(\w+)\s*(\(?-?0x[a-fA-F\d]+\)?)/$1\t=\t$2\n/;
		s/\s*#\s*define\s+(\w+)\s*(\(?-?[\d\.]+\)?)/$1\t=\t$2\n/;

		# z wartoscia nienumeryczna; zasieg: preprocesor (with a non-number value; scope: preprocessor)
		s/\s*#\s*define\s+(\w+)\s*([\w\"\(\)\-\+]*)/$1\tequ\t$2/;

		if ( $drukuj ) { print WY; }
	}

	#	Kompilacja warunkowa (conditional compiling)
	if ( /^\s*#\s*ifdef/ ) {

		s/\s*#\s*ifdef(.*)$/if defined $1/;
		if ( $drukuj ) { print WY; }
	}

	if ( /^\s*#\s*ifndef/ ) {

		# zgodnie z podrecznikiem (according to the manual)
		s/\s*#\s*ifndef(.*)$/if ~defined $1 | defined \@f\n\@\@:/;
		if ( $drukuj ) { print WY; }
	}

	if ( /^\s*#\s*endif/ ) {

		s/\s*#\s*endif.*$/end if/;
		if ( $drukuj ) { print WY; }
	}

	if ( /^\s*#\s*else\s+if/ ) {

	#	Operatory logiczne, mod i div (logical operators, mod and div):
		s/&&/&/g;
		s/\|\|/|/g;
		s/!/~/g;
		s/%/mod/g;
		s/\//div/g;
		s/<</shl/g;
		s/>>/shr/g;

		s/\s*#\s*else\s+if(.*)$/else if $1/;
		if ( $drukuj ) { print WY; }
	}

	if ( /^\s*#\s*else/ ) {

		s/\s*#\s*else(.*)$/else $1/;
		if ( $drukuj ) { print WY; }
	}

	if ( /^\s*#\s*if/ ) {

	#	Operatory logiczne, mod i div (logical operators, mod and div):
		s/&&/&/g;
		s/\|\|/|/g;
		s/!/~/g;
		s/%/mod/g;
		s/\//div/g;
		s/<</shl/g;
		s/>>/shr/g;

		s/\s*#\s*if(.*)$/if $1/;
		s/~\s*defined\s+(\w+)/(~defined $1 | defined \@f)/g;
		if ( /~defined/ )	{ s/$/\n\@\@:\n/; };
		if ( $drukuj ) { print WY; }
	}

	if ( /^\s*#\s*error/ || /^\s*#\s*warning/ ) {

		s/\s*#\s*(error|warning)(.*)$/display "$2"/;
		if ( $drukuj ) { print WY; }
	}

}

##########################################################
# Unie (unions)

sub zrob_unie {

		# parametr = 0 => nie ma drukowania (when the parameter is 0, there will be no printing)
		my $drukuj = shift;

		# pomijamy (skip over) "union foo;"
		if ( /\s*union\s*\w+\s*;/ ) { return 0; }

		# nazwa unii (the name of the union)
		s/\s*union\s+(\w+)/struc\t$1 /;

		my $nazwa;
		if ( $1 and $1 ne "" and $1 !~ /\{/ ) { $nazwa = $1; }
		else { $nazwa = ""; }
		my $rozmiar = 0;

		# jesli byl '{' w pierwszej linii, dajemy go do drugiej
		# (if there was a '{' in the first line, we put it in the second)
		if ( !/union/ ) { s/\s*\{/\n\{\n/; }
		else {
			s/\s*union\s*{//;
			s/\s*union//;
		};

		# unia moze sie konczyc w tej samej linijce lub zawierac wiele deklaracji na linie
		#   po prostu stawiamy znak nowej linii za kazdym srednikiem i idziemy dalej
		# (an union can end in the same line or contain many declaration per line
		#   we simply put a newline after each semicolon and go on)

		s/;/;\n/g;

		do {
			if ( $nazwa eq "" ) { s/\{//g; }

			# zmiana komentarzy (changing the comments)
			&komen;

			# typ wskaznikowy (pointer type)
			if ( /.+\*\s*(\w+)\s*;/ ) {
				s/.+\*\s*(\w+)\s*;/\t\.$1:/g;
				$rozmiar = max($rozmiar,4);
			}

			# typy zmiennych (variables' types)
			# wszystkie "\w+" oznaczaja nazwe zmiennej (all "\w+" stand for the variable name)
#			s/.*struct\s+(\w+)\s+(\w+)\s*;/\t\.$2\t\t$1/g;

			if ( /.*(signed|unsigned)?\s+long\s+long(\s+int)?\s+(\w+)\s*;/ ) {
				s/.*(signed|unsigned)?\s+long\s+long(\s+int)?\s+(\w+)\s*;/\t\.$3:/g;
				$rozmiar = max($rozmiar,8);
			}

			if ( /.*long\s+double\s+(\w+)\s*;/ ) {
				s/.*long\s+double\s+(\w+)\s*;/\t\.$1:/g;
				$rozmiar = max($rozmiar,10);
			}

			if ( /.*(char|unsigned\s+char|signed\s+char)\s+(\w+)\s*;/ ) {
				s/.*(char|unsigned\s+char|signed\s+char)\s+(\w+)\s*;/\t\.$2:/g;
				$rozmiar = max($rozmiar,1);
			}

			if ( /.*float\s+(\w+)\s*;/ ) {
				s/.*float\s+(\w+)\s*;/\t\.$1:/g;
				$rozmiar = max($rozmiar,4);
			}

			if ( /.*double\s+(\w+)\s*;/ ) {
				s/.*double\s+(\w+)\s*;/\t\.$1:/g;
				$rozmiar = max($rozmiar,8);
			}

			if ( /.*(short|signed\s+short|unsigned\s+short){1}(\s+int)?\s+(\w+)\s*;/ ) {
				s/.*(short|signed\s+short|unsigned\s+short){1}(\s+int)?\s+(\w+)\s*;/\t\.$3:/g;
				$rozmiar = max($rozmiar,2);
			}

			if ( /.*(long|signed|signed\s+long|unsigned|unsigned\s+long|int){1}(\s+int)?\s+(\w+)\s*;/ ) {
				s/.*(long|signed|signed\s+long|unsigned|unsigned\s+long|int){1}(\s+int)?\s+(\w+)\s*;/\t\.$3:/g;
				$rozmiar = max($rozmiar,4);
			}

			# tablice (arrays)

			if ( /.*(signed|unsigned)?\s+long\s+long(\s+int)?\s+(\w+)\s*\[(\d+)\]\s*;/ ) {
				s/.*(signed|unsigned)?\s+long\s+long(\s+int)?\s+(\w+)\s*\[(\d+)\]\s*;/\t\.$3:/g;
				$rozmiar = max($rozmiar,8*$4);
			}

			if ( /.*long\s+double\s+(\w+)\s*\[(\d+)\]\s*;/ ) {
				s/.*long\s+double\s+(\w+)\s*\[(\d+)\]\s*;/\t\.$1:/g;
				$rozmiar = max($rozmiar,10*$2);
			}

			if ( /.*(char|unsigned\s+char|signed\s+char)\s+(\w+)\s*\[(\d+)\]\s*;/ ) {
				s/.*(char|unsigned\s+char|signed\s+char)\s+(\w+)\s*\[(\d+)\]\s*;/\t\.$2:/g;
				$rozmiar = max($rozmiar,1*$3);
			}

			if ( /.*float\s+(\w+)\s*\[(\d+)\]\s*;/ ) {
				s/.*float\s+(\w+)\s*\[(\d+)\]\s*;/\t\.$1:/g;
				$rozmiar = max($rozmiar,4*$2);
			}

			if ( /.*double\s+(\w+)\s*\[(\d+)\]\s*;/ ) {
				s/.*double\s+(\w+)\s*\[(\d+)\]\s*;/\t\.$1:/g;
				$rozmiar = max($rozmiar,8*$2);
			}

			if ( /.*(short|signed\s+short|unsigned\s+short){1}(\s+int)?\s+(\w+)\s*\[(\d+)\]\s*;/ ) {
				s/.*(short|signed\s+short|unsigned\s+short){1}(\s+int)?\s+(\w+)\s*\[(\d+)\]\s*;/\t\.$3:/g;
				$rozmiar = max($rozmiar,2*$4);
			}

			if ( /.*(long|signed|signed\s+long|unsigned|unsigned\s+long|int){1}(\s+int)?\s+(\w+)\s*\[(\d+)\]\s*;/ ) {
				s/.*(long|signed|signed\s+long|unsigned|unsigned\s+long|int){1}(\s+int)?\s+(\w+)\s*\[(\d+)\]\s*;/\t\.$3:/g;
				$rozmiar = max($rozmiar,4*$4);
			}

			# szukamy konca unii (look for the end of the union)
			if ( /\s*\}.*/ ) {

				s/\s*\}\s*(\w+)\s*;/\t.$1:\n}\n/;
				s/\s*\}.*/\t\tresb\t$rozmiar/;
				if ( $nazwa ne "" ) { $_ .= "\n}\n"; }
				# dolaczamy definicje rozmiar unii (add an union size definition)
				#if ( $nazwa ne "" ) { $_ .= "\n\t${nazwa}_size\t=\t$rozmiar\n"; }
				if ( $drukuj ) { print WY; }
				return $rozmiar;
			}

			# przetwarzanie dyrektyw kompilacji warunkowej (processing of conditional compiling directives)
			&kom_war(0);
			if ( $drukuj ) { print WY; }

		} while ( <WE> );

}

sub max {

	my $a=shift, $b=shift;
	return $a if $a > $b;
	return $b;
}


##########################################################
# Koniec (the end):


close WY;
close WE;

