Selaa lähdekoodia

fcl-css package

Michaël Van Canneyt 3 vuotta sitten
vanhempi
commit
5b3953dde3
34 muutettua tiedostoa jossa 10610 lisäystä ja 0 poistoa
  1. 2956 0
      packages/fcl-css/Makefile
  2. 102 0
      packages/fcl-css/Makefile.fpc
  3. 118 0
      packages/fcl-css/examples/cssmin.lpr
  4. 126 0
      packages/fcl-css/examples/extractcssclasses.lpr
  5. 64 0
      packages/fcl-css/fpmake.pp
  6. 897 0
      packages/fcl-css/src/fpcssparser.pp
  7. 828 0
      packages/fcl-css/src/fpcssscanner.pp
  8. 1068 0
      packages/fcl-css/src/fpcsstree.pp
  9. 196 0
      packages/fcl-css/src/fpcssutils.pp
  10. 7 0
      packages/fcl-css/tests/css/absolute.css
  11. 10 0
      packages/fcl-css/tests/css/animation.css
  12. 3 0
      packages/fcl-css/tests/css/anon.css
  13. 14 0
      packages/fcl-css/tests/css/bigbig.css
  14. 21 0
      packages/fcl-css/tests/css/class.css
  15. 3 0
      packages/fcl-css/tests/css/color.css
  16. 7 0
      packages/fcl-css/tests/css/font-face.css
  17. 24 0
      packages/fcl-css/tests/css/font-face2.css
  18. 8 0
      packages/fcl-css/tests/css/font.css
  19. 49 0
      packages/fcl-css/tests/css/hello.css
  20. 3 0
      packages/fcl-css/tests/css/id.css
  21. 8 0
      packages/fcl-css/tests/css/input_type.css
  22. 8 0
      packages/fcl-css/tests/css/margin.css
  23. 7 0
      packages/fcl-css/tests/css/media_query.css
  24. 1193 0
      packages/fcl-css/tests/css/mystyle.css
  25. 107 0
      packages/fcl-css/tests/css/news.css
  26. 8 0
      packages/fcl-css/tests/css/padding.css
  27. 206 0
      packages/fcl-css/tests/css/style.css
  28. 51 0
      packages/fcl-css/tests/css/style2.css
  29. 175 0
      packages/fcl-css/tests/css/style_big.css
  30. 5 0
      packages/fcl-css/tests/css/wildcard.css
  31. 807 0
      packages/fcl-css/tests/tccssparser.pp
  32. 710 0
      packages/fcl-css/tests/tccssscanner.pp
  33. 792 0
      packages/fcl-css/tests/tccsstree.pp
  34. 29 0
      packages/fcl-css/tests/testcss.lpr

+ 2956 - 0
packages/fcl-css/Makefile

@@ -0,0 +1,2956 @@
+#
+# Don't edit, this file is generated by FPCMake Version 2.0.0
+#
+default: all
+MAKEFILETARGETS=i386-linux i386-go32v2 i386-win32 i386-os2 i386-freebsd i386-beos i386-haiku i386-netbsd i386-solaris i386-netware i386-openbsd i386-wdosx i386-darwin i386-emx i386-watcom i386-netwlibc i386-wince i386-embedded i386-symbian i386-nativent i386-iphonesim i386-android i386-aros m68k-linux m68k-netbsd m68k-amiga m68k-atari m68k-palmos m68k-macosclassic m68k-embedded m68k-sinclairql powerpc-linux powerpc-netbsd powerpc-amiga powerpc-macosclassic powerpc-darwin powerpc-morphos powerpc-embedded powerpc-wii powerpc-aix sparc-linux sparc-netbsd sparc-solaris sparc-embedded x86_64-linux x86_64-freebsd x86_64-haiku x86_64-netbsd x86_64-solaris x86_64-openbsd x86_64-darwin x86_64-win64 x86_64-embedded x86_64-iphonesim x86_64-android x86_64-aros x86_64-dragonfly arm-linux arm-netbsd arm-palmos arm-wince arm-gba arm-nds arm-embedded arm-symbian arm-android arm-aros arm-freertos arm-ios powerpc64-linux powerpc64-darwin powerpc64-embedded powerpc64-aix avr-embedded armeb-linux armeb-embedded mips-linux mipsel-linux mipsel-embedded mipsel-android mips64el-linux jvm-java jvm-android i8086-embedded i8086-msdos i8086-win16 aarch64-linux aarch64-freebsd aarch64-darwin aarch64-win64 aarch64-android aarch64-ios wasm32-embedded wasm32-wasi sparc64-linux riscv32-linux riscv32-embedded riscv64-linux riscv64-embedded xtensa-linux xtensa-embedded xtensa-freertos z80-embedded z80-zxspectrum z80-msxdos z80-amstradcpc
+BSDs = freebsd netbsd openbsd darwin dragonfly
+UNIXs = linux $(BSDs) solaris qnx haiku aix
+LIMIT83fs = go32v2 os2 emx watcom msdos win16 atari
+OSNeedsComspecToRunBatch = go32v2 watcom
+FORCE:
+.PHONY: FORCE
+override PATH:=$(patsubst %/,%,$(subst \,/,$(PATH)))
+ifneq ($(findstring darwin,$(OSTYPE)),)
+inUnix=1 #darwin
+SEARCHPATH:=$(filter-out .,$(subst :, ,$(PATH)))
+else
+ifeq ($(findstring ;,$(PATH)),)
+inUnix=1
+SEARCHPATH:=$(filter-out .,$(subst :, ,$(PATH)))
+else
+SEARCHPATH:=$(subst ;, ,$(PATH))
+endif
+endif
+SEARCHPATH+=$(patsubst %/,%,$(subst \,/,$(dir $(MAKE))))
+PWD:=$(strip $(wildcard $(addsuffix /pwd.exe,$(SEARCHPATH))))
+ifeq ($(PWD),)
+PWD:=$(strip $(wildcard $(addsuffix /pwd,$(SEARCHPATH))))
+ifeq ($(PWD),)
+$(error You need the GNU utils package to use this Makefile)
+else
+PWD:=$(firstword $(PWD))
+SRCEXEEXT=
+endif
+else
+PWD:=$(firstword $(PWD))
+SRCEXEEXT=.exe
+endif
+ifndef inUnix
+ifeq ($(OS),Windows_NT)
+inWinNT=1
+else
+ifdef OS2_SHELL
+inOS2=1
+endif
+endif
+else
+ifneq ($(findstring cygdrive,$(PATH)),)
+inCygWin=1
+endif
+endif
+ifdef inUnix
+SRCBATCHEXT=.sh
+else
+ifdef inOS2
+SRCBATCHEXT=.cmd
+else
+SRCBATCHEXT=.bat
+endif
+endif
+ifdef COMSPEC
+ifneq ($(findstring $(OS_SOURCE),$(OSNeedsComspecToRunBatch)),)
+ifndef RUNBATCH
+RUNBATCH=$(COMSPEC) /C
+endif
+endif
+endif
+ifdef inUnix
+PATHSEP=/
+else
+PATHSEP:=$(subst /,\,/)
+ifdef inCygWin
+PATHSEP=/
+endif
+endif
+ifdef PWD
+BASEDIR:=$(subst \,/,$(shell $(PWD)))
+ifdef inCygWin
+ifneq ($(findstring /cygdrive/,$(BASEDIR)),)
+BASENODIR:=$(patsubst /cygdrive%,%,$(BASEDIR))
+BASEDRIVE:=$(firstword $(subst /, ,$(BASENODIR)))
+BASEDIR:=$(subst /cygdrive/$(BASEDRIVE)/,$(BASEDRIVE):/,$(BASEDIR))
+endif
+endif
+else
+BASEDIR=.
+endif
+ifdef inOS2
+ifndef ECHO
+ECHO:=$(strip $(wildcard $(addsuffix /gecho$(SRCEXEEXT),$(SEARCHPATH))))
+ifeq ($(ECHO),)
+ECHO:=$(strip $(wildcard $(addsuffix /echo$(SRCEXEEXT),$(SEARCHPATH))))
+ifeq ($(ECHO),)
+ECHO=echo
+else
+ECHO:=$(firstword $(ECHO))
+endif
+else
+ECHO:=$(firstword $(ECHO))
+endif
+endif
+export ECHO
+endif
+override DEFAULT_FPCDIR=../..
+ifndef FPC
+ifdef PP
+FPC=$(PP)
+endif
+endif
+ifndef FPC
+FPCPROG:=$(strip $(wildcard $(addsuffix /fpc$(SRCEXEEXT),$(SEARCHPATH))))
+ifneq ($(FPCPROG),)
+FPCPROG:=$(firstword $(FPCPROG))
+ifneq ($(CPU_TARGET),)
+FPC:=$(shell $(FPCPROG) -P$(CPU_TARGET) -PB)
+else
+FPC:=$(shell $(FPCPROG) -PB)
+endif
+ifneq ($(findstring Error,$(FPC)),)
+override FPC=$(firstword $(strip $(wildcard $(addsuffix /ppc386$(SRCEXEEXT),$(SEARCHPATH)))))
+else
+ifeq ($(strip $(wildcard $(FPC))),)
+FPC:=$(firstword $(FPCPROG))
+endif
+endif
+else
+override FPC=$(firstword $(strip $(wildcard $(addsuffix /ppc386$(SRCEXEEXT),$(SEARCHPATH)))))
+endif
+endif
+override FPC:=$(subst $(SRCEXEEXT),,$(FPC))
+override FPC:=$(subst \,/,$(FPC))$(SRCEXEEXT)
+FOUNDFPC:=$(strip $(wildcard $(FPC)))
+ifeq ($(FOUNDFPC),)
+FOUNDFPC=$(strip $(wildcard $(addsuffix /$(FPC),$(SEARCHPATH))))
+ifeq ($(FOUNDFPC),)
+$(error Compiler $(FPC) not found)
+endif
+endif
+ifndef FPC_COMPILERINFO
+FPC_COMPILERINFO:=$(shell $(FPC) -iVSPTPSOTO)
+endif
+ifndef FPC_VERSION
+FPC_VERSION:=$(word 1,$(FPC_COMPILERINFO))
+endif
+export FPC FPC_VERSION FPC_COMPILERINFO
+unexport CHECKDEPEND ALLDEPENDENCIES
+ifndef CPU_TARGET
+ifdef CPU_TARGET_DEFAULT
+CPU_TARGET=$(CPU_TARGET_DEFAULT)
+endif
+endif
+ifndef OS_TARGET
+ifdef OS_TARGET_DEFAULT
+OS_TARGET=$(OS_TARGET_DEFAULT)
+endif
+endif
+ifndef CPU_SOURCE
+CPU_SOURCE:=$(word 2,$(FPC_COMPILERINFO))
+endif
+ifndef CPU_TARGET
+CPU_TARGET:=$(word 3,$(FPC_COMPILERINFO))
+endif
+ifndef OS_SOURCE
+OS_SOURCE:=$(word 4,$(FPC_COMPILERINFO))
+endif
+ifndef OS_TARGET
+OS_TARGET:=$(word 5,$(FPC_COMPILERINFO))
+endif
+FULL_TARGET=$(CPU_TARGET)-$(OS_TARGET)
+FULL_SOURCE=$(CPU_SOURCE)-$(OS_SOURCE)
+ifeq ($(CPU_TARGET),armeb)
+ARCH=arm
+override FPCOPT+=-Cb
+else
+ifeq ($(CPU_TARGET),armel)
+ARCH=arm
+override FPCOPT+=-CaEABI
+else
+ARCH=$(CPU_TARGET)
+endif
+endif
+ifeq ($(FULL_TARGET),arm-embedded)
+ifeq ($(SUBARCH),)
+$(error When compiling for arm-embedded, a sub-architecture (e.g. SUBARCH=armv4t or SUBARCH=armv7m) must be defined)
+endif
+override FPCOPT+=-Cp$(SUBARCH)
+endif
+ifeq ($(FULL_TARGET),avr-embedded)
+ifeq ($(SUBARCH),)
+$(error When compiling for avr-embedded, a sub-architecture (e.g. SUBARCH=avr25 or SUBARCH=avr35) must be defined)
+endif
+override FPCOPT+=-Cp$(SUBARCH)
+endif
+ifeq ($(FULL_TARGET),mipsel-embedded)
+ifeq ($(SUBARCH),)
+$(error When compiling for mipsel-embedded, a sub-architecture (e.g. SUBARCH=pic32mx) must be defined)
+endif
+override FPCOPT+=-Cp$(SUBARCH)
+endif
+ifeq ($(FULL_TARGET),xtensa-embedded)
+ifeq ($(SUBARCH),)
+$(error When compiling for xtensa-embedded, a sub-architecture (e.g. SUBARCH=lx106 or SUBARCH=lx6) must be defined)
+endif
+override FPCOPT+=-Cp$(SUBARCH)
+endif
+ifeq ($(FULL_TARGET),xtensa-freertos)
+ifeq ($(SUBARCH),)
+$(error When compiling for xtensa-freertos, a sub-architecture (e.g. SUBARCH=lx106 or SUBARCH=lx6) must be defined)
+endif
+override FPCOPT+=-Cp$(SUBARCH)
+endif
+ifeq ($(FULL_TARGET),arm-freertos)
+ifeq ($(SUBARCH),)
+$(error When compiling for arm-freertos, a sub-architecture (e.g. SUBARCH=armv6m or SUBARCH=armv7em) must be defined)
+endif
+override FPCOPT+=-Cp$(SUBARCH)
+endif
+ifneq ($(findstring $(OS_SOURCE),$(LIMIT83fs)),)
+TARGETSUFFIX=$(OS_TARGET)
+SOURCESUFFIX=$(OS_SOURCE)
+else
+ifneq ($(findstring $(OS_TARGET),$(LIMIT83fs)),)
+TARGETSUFFIX=$(OS_TARGET)
+else
+TARGETSUFFIX=$(FULL_TARGET)
+endif
+SOURCESUFFIX=$(FULL_SOURCE)
+endif
+ifneq ($(FULL_TARGET),$(FULL_SOURCE))
+CROSSCOMPILE=1
+endif
+ifeq ($(findstring makefile,$(MAKECMDGOALS)),)
+ifeq ($(findstring $(FULL_TARGET),$(MAKEFILETARGETS)),)
+$(error The Makefile doesn't support target $(FULL_TARGET), please run fpcmake first)
+endif
+endif
+ifneq ($(findstring $(OS_TARGET),$(BSDs)),)
+BSDhier=1
+endif
+ifeq ($(OS_TARGET),linux)
+linuxHier=1
+endif
+ifndef CROSSCOMPILE
+BUILDFULLNATIVE=1
+export BUILDFULLNATIVE
+endif
+ifdef BUILDFULLNATIVE
+BUILDNATIVE=1
+export BUILDNATIVE
+endif
+export OS_TARGET OS_SOURCE ARCH CPU_TARGET CPU_SOURCE FULL_TARGET FULL_SOURCE TARGETSUFFIX SOURCESUFFIX CROSSCOMPILE
+ifdef FPCDIR
+override FPCDIR:=$(subst \,/,$(FPCDIR))
+ifeq ($(wildcard $(addprefix $(FPCDIR)/,rtl)),)
+override FPCDIR=wrong
+endif
+else
+override FPCDIR=wrong
+endif
+ifdef DEFAULT_FPCDIR
+ifeq ($(FPCDIR),wrong)
+override FPCDIR:=$(subst \,/,$(DEFAULT_FPCDIR))
+ifeq ($(wildcard $(addprefix $(FPCDIR)/,rtl)),)
+override FPCDIR=wrong
+endif
+endif
+endif
+ifeq ($(FPCDIR),wrong)
+ifdef inUnix
+override FPCDIR=/usr/local/lib/fpc/$(FPC_VERSION)
+ifeq ($(wildcard $(FPCDIR)/units),)
+override FPCDIR=/usr/lib/fpc/$(FPC_VERSION)
+endif
+else
+override FPCDIR:=$(subst /$(FPC),,$(firstword $(strip $(wildcard $(addsuffix /$(FPC),$(SEARCHPATH))))))
+override FPCDIR:=$(FPCDIR)/..
+ifeq ($(wildcard $(addprefix $(FPCDIR)/,rtl)),)
+override FPCDIR:=$(FPCDIR)/..
+ifeq ($(wildcard $(addprefix $(FPCDIR)/,rtl)),)
+override FPCDIR:=$(BASEDIR)
+ifeq ($(wildcard $(addprefix $(FPCDIR)/,rtl)),)
+override FPCDIR=c:/pp
+endif
+endif
+endif
+endif
+endif
+ifndef CROSSBINDIR
+CROSSBINDIR:=$(wildcard $(FPCDIR)/bin/$(TARGETSUFFIX))
+endif
+ifneq ($(findstring $(OS_TARGET),darwin iphonesim ios),)
+ifneq ($(findstring $(OS_SOURCE),darwin ios),)
+DARWIN2DARWIN=1
+endif
+endif
+ifndef BINUTILSPREFIX
+ifndef CROSSBINDIR
+ifdef CROSSCOMPILE
+ifneq ($(OS_TARGET),msdos)
+ifndef DARWIN2DARWIN
+ifneq ($(CPU_TARGET),jvm)
+BINUTILSPREFIX=$(CPU_TARGET)-$(OS_TARGET)-
+ifeq ($(OS_TARGET),android)
+ifeq ($(CPU_TARGET),arm)
+BINUTILSPREFIX=arm-linux-androideabi-
+else
+ifeq ($(CPU_TARGET),i386)
+BINUTILSPREFIX=i686-linux-android-
+else
+BINUTILSPREFIX=$(CPU_TARGET)-linux-android-
+endif
+endif
+endif
+endif
+endif
+else
+BINUTILSPREFIX=$(OS_TARGET)-
+endif
+endif
+endif
+endif
+UNITSDIR:=$(wildcard $(FPCDIR)/units/$(TARGETSUFFIX))
+ifeq ($(UNITSDIR),)
+UNITSDIR:=$(wildcard $(FPCDIR)/units/$(OS_TARGET))
+endif
+PACKAGESDIR:=$(wildcard $(FPCDIR) $(FPCDIR)/packages)
+ifndef FPCFPMAKE
+ifdef CROSSCOMPILE
+ifeq ($(strip $(wildcard $(addsuffix /compiler/ppc$(SRCEXEEXT),$(FPCDIR)))),)
+FPCPROG:=$(strip $(wildcard $(addsuffix /fpc$(SRCEXEEXT),$(SEARCHPATH))))
+ifneq ($(FPCPROG),)
+FPCPROG:=$(firstword $(FPCPROG))
+FPCFPMAKE:=$(shell $(FPCPROG) -PB)
+ifeq ($(strip $(wildcard $(FPCFPMAKE))),)
+FPCFPMAKE:=$(firstword $(FPCPROG))
+endif
+else
+override FPCFPMAKE=$(firstword $(strip $(wildcard $(addsuffix /ppc386$(SRCEXEEXT),$(SEARCHPATH)))))
+endif
+else
+FPCFPMAKE=$(strip $(wildcard $(addsuffix /compiler/ppc$(SRCEXEEXT),$(FPCDIR))))
+FPMAKE_SKIP_CONFIG=-n
+export FPCFPMAKE
+export FPMAKE_SKIP_CONFIG
+endif
+else
+FPMAKE_SKIP_CONFIG=-n
+FPCFPMAKE=$(FPC)
+endif
+endif
+override PACKAGE_NAME=fcl-css
+override PACKAGE_VERSION=3.3.1
+FPMAKE_BIN_CLEAN=$(wildcard ./fpmake$(SRCEXEEXT))
+ifdef OS_TARGET
+FPC_TARGETOPT+=--os=$(OS_TARGET)
+endif
+ifdef CPU_TARGET
+FPC_TARGETOPT+=--cpu=$(CPU_TARGET)
+endif
+LOCALFPMAKE=./fpmake$(SRCEXEEXT)
+override INSTALL_FPCPACKAGE=y
+ifdef REQUIRE_UNITSDIR
+override UNITSDIR+=$(REQUIRE_UNITSDIR)
+endif
+ifdef REQUIRE_PACKAGESDIR
+override PACKAGESDIR+=$(REQUIRE_PACKAGESDIR)
+endif
+ifdef ZIPINSTALL
+ifneq ($(findstring $(OS_TARGET),$(UNIXs)),)
+UNIXHier=1
+endif
+else
+ifneq ($(findstring $(OS_SOURCE),$(UNIXs)),)
+UNIXHier=1
+endif
+endif
+ifndef INSTALL_PREFIX
+ifdef PREFIX
+INSTALL_PREFIX=$(PREFIX)
+endif
+endif
+ifndef INSTALL_PREFIX
+ifdef UNIXHier
+INSTALL_PREFIX=/usr/local
+else
+ifdef INSTALL_FPCPACKAGE
+INSTALL_BASEDIR:=/pp
+else
+INSTALL_BASEDIR:=/$(PACKAGE_NAME)
+endif
+endif
+endif
+export INSTALL_PREFIX
+ifdef INSTALL_FPCSUBDIR
+export INSTALL_FPCSUBDIR
+endif
+ifndef DIST_DESTDIR
+DIST_DESTDIR:=$(BASEDIR)
+endif
+export DIST_DESTDIR
+ifndef COMPILER_UNITTARGETDIR
+ifdef PACKAGEDIR_MAIN
+COMPILER_UNITTARGETDIR=$(PACKAGEDIR_MAIN)/units/$(TARGETSUFFIX)
+else
+COMPILER_UNITTARGETDIR=units/$(TARGETSUFFIX)
+endif
+endif
+ifndef COMPILER_TARGETDIR
+COMPILER_TARGETDIR=.
+endif
+ifndef INSTALL_BASEDIR
+ifdef UNIXHier
+ifdef INSTALL_FPCPACKAGE
+INSTALL_BASEDIR:=$(INSTALL_PREFIX)/lib/fpc/$(FPC_VERSION)
+else
+INSTALL_BASEDIR:=$(INSTALL_PREFIX)/lib/$(PACKAGE_NAME)
+endif
+else
+INSTALL_BASEDIR:=$(INSTALL_PREFIX)
+endif
+endif
+ifndef INSTALL_BINDIR
+ifdef UNIXHier
+INSTALL_BINDIR:=$(INSTALL_PREFIX)/bin
+else
+INSTALL_BINDIR:=$(INSTALL_BASEDIR)/bin
+ifdef INSTALL_FPCPACKAGE
+ifdef CROSSCOMPILE
+ifdef CROSSINSTALL
+INSTALL_BINDIR:=$(INSTALL_BINDIR)/$(SOURCESUFFIX)
+else
+INSTALL_BINDIR:=$(INSTALL_BINDIR)/$(TARGETSUFFIX)
+endif
+else
+INSTALL_BINDIR:=$(INSTALL_BINDIR)/$(TARGETSUFFIX)
+endif
+endif
+endif
+endif
+ifndef INSTALL_UNITDIR
+INSTALL_UNITDIR:=$(INSTALL_BASEDIR)/units/$(TARGETSUFFIX)
+ifdef INSTALL_FPCPACKAGE
+ifdef PACKAGE_NAME
+INSTALL_UNITDIR:=$(INSTALL_UNITDIR)/$(PACKAGE_NAME)
+endif
+endif
+endif
+ifndef INSTALL_LIBDIR
+ifdef UNIXHier
+INSTALL_LIBDIR:=$(INSTALL_PREFIX)/lib
+else
+INSTALL_LIBDIR:=$(INSTALL_UNITDIR)
+endif
+endif
+ifndef INSTALL_SOURCEDIR
+ifdef UNIXHier
+ifdef BSDhier
+SRCPREFIXDIR=share/src
+else
+ifdef linuxHier
+SRCPREFIXDIR=share/src
+else
+SRCPREFIXDIR=src
+endif
+endif
+ifdef INSTALL_FPCPACKAGE
+ifdef INSTALL_FPCSUBDIR
+INSTALL_SOURCEDIR:=$(INSTALL_PREFIX)/$(SRCPREFIXDIR)/fpc-$(FPC_VERSION)/$(INSTALL_FPCSUBDIR)/$(PACKAGE_NAME)
+else
+INSTALL_SOURCEDIR:=$(INSTALL_PREFIX)/$(SRCPREFIXDIR)/fpc-$(FPC_VERSION)/$(PACKAGE_NAME)
+endif
+else
+INSTALL_SOURCEDIR:=$(INSTALL_PREFIX)/$(SRCPREFIXDIR)/$(PACKAGE_NAME)-$(PACKAGE_VERSION)
+endif
+else
+ifdef INSTALL_FPCPACKAGE
+ifdef INSTALL_FPCSUBDIR
+INSTALL_SOURCEDIR:=$(INSTALL_BASEDIR)/source/$(INSTALL_FPCSUBDIR)/$(PACKAGE_NAME)
+else
+INSTALL_SOURCEDIR:=$(INSTALL_BASEDIR)/source/$(PACKAGE_NAME)
+endif
+else
+INSTALL_SOURCEDIR:=$(INSTALL_BASEDIR)/source
+endif
+endif
+endif
+ifndef INSTALL_DOCDIR
+ifdef UNIXHier
+ifdef BSDhier
+DOCPREFIXDIR=share/doc
+else
+ifdef linuxHier
+DOCPREFIXDIR=share/doc
+else
+DOCPREFIXDIR=doc
+endif
+endif
+ifdef INSTALL_FPCPACKAGE
+INSTALL_DOCDIR:=$(INSTALL_PREFIX)/$(DOCPREFIXDIR)/fpc-$(FPC_VERSION)/$(PACKAGE_NAME)
+else
+INSTALL_DOCDIR:=$(INSTALL_PREFIX)/$(DOCPREFIXDIR)/$(PACKAGE_NAME)-$(PACKAGE_VERSION)
+endif
+else
+ifdef INSTALL_FPCPACKAGE
+INSTALL_DOCDIR:=$(INSTALL_BASEDIR)/doc/$(PACKAGE_NAME)
+else
+INSTALL_DOCDIR:=$(INSTALL_BASEDIR)/doc
+endif
+endif
+endif
+ifndef INSTALL_EXAMPLEDIR
+ifdef UNIXHier
+ifdef INSTALL_FPCPACKAGE
+ifdef BSDhier
+INSTALL_EXAMPLEDIR:=$(INSTALL_PREFIX)/share/examples/fpc-$(FPC_VERSION)/$(PACKAGE_NAME)
+else
+ifdef linuxHier
+INSTALL_EXAMPLEDIR:=$(INSTALL_DOCDIR)/examples
+else
+INSTALL_EXAMPLEDIR:=$(INSTALL_PREFIX)/doc/fpc-$(FPC_VERSION)/examples/$(PACKAGE_NAME)
+endif
+endif
+else
+ifdef BSDhier
+INSTALL_EXAMPLEDIR:=$(INSTALL_PREFIX)/share/examples/$(PACKAGE_NAME)-$(PACKAGE_VERSION)
+else
+ifdef linuxHier
+INSTALL_EXAMPLEDIR:=$(INSTALL_DOCDIR)/examples/$(PACKAGE_NAME)-$(PACKAGE_VERSION)
+else
+INSTALL_EXAMPLEDIR:=$(INSTALL_PREFIX)/doc/$(PACKAGE_NAME)-$(PACKAGE_VERSION)
+endif
+endif
+endif
+else
+ifdef INSTALL_FPCPACKAGE
+INSTALL_EXAMPLEDIR:=$(INSTALL_BASEDIR)/examples/$(PACKAGE_NAME)
+else
+INSTALL_EXAMPLEDIR:=$(INSTALL_BASEDIR)/examples
+endif
+endif
+endif
+ifndef INSTALL_DATADIR
+INSTALL_DATADIR=$(INSTALL_BASEDIR)
+endif
+ifndef INSTALL_SHAREDDIR
+INSTALL_SHAREDDIR=$(INSTALL_PREFIX)/lib
+endif
+ifdef CROSSCOMPILE
+ifndef CROSSBINDIR
+CROSSBINDIR:=$(wildcard $(CROSSTARGETDIR)/bin/$(SOURCESUFFIX))
+ifeq ($(CROSSBINDIR),)
+CROSSBINDIR:=$(wildcard $(INSTALL_BASEDIR)/cross/$(TARGETSUFFIX)/bin/$(FULL_SOURCE))
+endif
+endif
+else
+CROSSBINDIR=
+endif
+ifeq ($(OS_SOURCE),linux)
+ifndef GCCLIBDIR
+ifeq ($(CPU_TARGET),i386)
+ifneq ($(findstring x86_64,$(shell uname -a)),)
+ifeq ($(BINUTILSPREFIX),)
+GCCLIBDIR:=$(shell dirname `gcc -m32 -print-libgcc-file-name`)
+else
+CROSSGCCOPT=-m32
+endif
+endif
+endif
+ifeq ($(CPU_TARGET),powerpc)
+ifeq ($(BINUTILSPREFIX),)
+GCCLIBDIR:=$(shell dirname `gcc -m32 -print-libgcc-file-name`)
+else
+CROSSGCCOPT=-m32
+endif
+endif
+ifeq ($(CPU_TARGET),powerpc64)
+ifeq ($(BINUTILSPREFIX),)
+GCCLIBDIR:=$(shell dirname `gcc -m64 -print-libgcc-file-name`)
+else
+CROSSGCCOPT=-m64
+endif
+endif
+ifeq ($(CPU_TARGET),sparc)
+ifneq ($(findstring sparc64,$(shell uname -a)),)
+ifeq ($(BINUTILSPREFIX),)
+GCCLIBDIR:=$(shell dirname `gcc -m32 -print-libgcc-file-name`)
+else
+ifneq ($(findstring $(FPCFPMAKE_CPU_OPT),mips mipsel),)
+CROSSGCCOPT=-mabi=32
+else
+CROSSGCCOPT=-m32
+endif
+endif
+endif
+endif
+endif
+ifdef FPCFPMAKE
+FPCFPMAKE_CPU_TARGET=$(shell $(FPCFPMAKE) -iTP)
+ifeq ($(CPU_TARGET),$(FPCFPMAKE_CPU_TARGET))
+FPCMAKEGCCLIBDIR:=$(GCCLIBDIR)
+else
+ifneq ($(findstring $(FPCFPMAKE_CPU_TARGET),aarch64 powerpc64 riscv64 sparc64 x86_64),)
+FPCMAKE_CROSSGCCOPT=-m64
+else
+ifneq ($(findstring $(FPCFPMAKE_CPU_OPT),mips64 mips64el),)
+FPCMAKE_CROSSGCCOPT=-mabi=64
+else
+ifneq ($(findstring $(FPCFPMAKE_CPU_OPT),mips mipsel),)
+FPCMAKE_CROSSGCCOPT=-mabi=32
+else
+ifneq ($(findstring $(FPCFPMAKE_CPU_OPT),riscv64),)
+FPCMAKE_CROSSGCCOPT=-mabi=lp64
+else
+ifneq ($(findstring $(FPCFPMAKE_CPU_OPT),riscv32),)
+FPCMAKE_CROSSGCCOPT=-mabi=ilp32
+else
+FPCMAKE_CROSSGCCOPT=-m32
+endif
+endif
+endif
+endif
+endif
+FPCMAKEGCCLIBDIR:=$(shell dirname `gcc $(FPCMAKE_CROSSGCCOPT) -print-libgcc-file-name`)
+endif
+endif
+ifndef FPCMAKEGCCLIBDIR
+FPCMAKEGCCLIBDIR:=$(shell dirname `gcc -print-libgcc-file-name`)
+endif
+ifndef GCCLIBDIR
+CROSSGCC=$(strip $(wildcard $(addsuffix /$(BINUTILSPREFIX)gcc$(SRCEXEEXT),$(SEARCHPATH))))
+ifneq ($(CROSSGCC),)
+GCCLIBDIR:=$(shell dirname `$(CROSSGCC) $(CROSSGCCOPT) -print-libgcc-file-name`)
+endif
+endif
+endif
+ifdef inUnix
+ifeq ($(OS_SOURCE),netbsd)
+OTHERLIBDIR:=/usr/pkg/lib
+endif
+export GCCLIBDIR FPCMAKEGCCLIBDIR OTHERLIBDIR
+endif
+BATCHEXT=.bat
+LOADEREXT=.as
+EXEEXT=.exe
+PPLEXT=.ppl
+PPUEXT=.ppu
+OEXT=.o
+LTOEXT=.bc
+ASMEXT=.s
+SMARTEXT=.sl
+STATICLIBEXT=.a
+SHAREDLIBEXT=.so
+SHAREDLIBPREFIX=libfp
+STATICLIBPREFIX=libp
+IMPORTLIBPREFIX=libimp
+RSTEXT=.rst
+EXEDBGEXT=.dbg
+ifeq ($(OS_TARGET),go32v1)
+STATICLIBPREFIX=
+SHORTSUFFIX=v1
+endif
+ifeq ($(OS_TARGET),go32v2)
+STATICLIBPREFIX=
+SHORTSUFFIX=dos
+IMPORTLIBPREFIX=
+endif
+ifeq ($(OS_TARGET),watcom)
+STATICLIBPREFIX=
+OEXT=.obj
+ASMEXT=.asm
+SHAREDLIBEXT=.dll
+SHORTSUFFIX=wat
+IMPORTLIBPREFIX=
+endif
+ifneq ($(CPU_TARGET),jvm)
+ifeq ($(OS_TARGET),android)
+BATCHEXT=.sh
+EXEEXT=
+HASSHAREDLIB=1
+SHORTSUFFIX=lnx
+endif
+endif
+ifeq ($(OS_TARGET),linux)
+BATCHEXT=.sh
+EXEEXT=
+HASSHAREDLIB=1
+SHORTSUFFIX=lnx
+endif
+ifeq ($(OS_TARGET),dragonfly)
+BATCHEXT=.sh
+EXEEXT=
+HASSHAREDLIB=1
+SHORTSUFFIX=df
+endif
+ifeq ($(OS_TARGET),freebsd)
+BATCHEXT=.sh
+EXEEXT=
+HASSHAREDLIB=1
+SHORTSUFFIX=fbs
+endif
+ifeq ($(OS_TARGET),netbsd)
+BATCHEXT=.sh
+EXEEXT=
+HASSHAREDLIB=1
+SHORTSUFFIX=nbs
+endif
+ifeq ($(OS_TARGET),openbsd)
+BATCHEXT=.sh
+EXEEXT=
+HASSHAREDLIB=1
+SHORTSUFFIX=obs
+endif
+ifeq ($(OS_TARGET),win32)
+SHAREDLIBEXT=.dll
+SHORTSUFFIX=w32
+endif
+ifeq ($(OS_TARGET),os2)
+BATCHEXT=.cmd
+AOUTEXT=.out
+STATICLIBPREFIX=
+SHAREDLIBEXT=.dll
+SHORTSUFFIX=os2
+ECHO=echo
+IMPORTLIBPREFIX=
+endif
+ifeq ($(OS_TARGET),emx)
+BATCHEXT=.cmd
+AOUTEXT=.out
+STATICLIBPREFIX=
+SHAREDLIBEXT=.dll
+SHORTSUFFIX=emx
+ECHO=echo
+IMPORTLIBPREFIX=
+endif
+ifeq ($(OS_TARGET),amiga)
+EXEEXT=
+SHAREDLIBEXT=.library
+SHORTSUFFIX=amg
+endif
+ifeq ($(OS_TARGET),aros)
+EXEEXT=
+SHAREDLIBEXT=.library
+SHORTSUFFIX=aros
+endif
+ifeq ($(OS_TARGET),morphos)
+EXEEXT=
+SHAREDLIBEXT=.library
+SHORTSUFFIX=mos
+endif
+ifeq ($(OS_TARGET),atari)
+EXEEXT=.ttp
+SHORTSUFFIX=ata
+endif
+ifeq ($(OS_TARGET),beos)
+BATCHEXT=.sh
+EXEEXT=
+SHORTSUFFIX=be
+endif
+ifeq ($(OS_TARGET),haiku)
+BATCHEXT=.sh
+EXEEXT=
+SHORTSUFFIX=hai
+endif
+ifeq ($(OS_TARGET),solaris)
+BATCHEXT=.sh
+EXEEXT=
+SHORTSUFFIX=sun
+endif
+ifeq ($(OS_TARGET),qnx)
+BATCHEXT=.sh
+EXEEXT=
+SHORTSUFFIX=qnx
+endif
+ifeq ($(OS_TARGET),netware)
+EXEEXT=.nlm
+STATICLIBPREFIX=
+SHORTSUFFIX=nw
+IMPORTLIBPREFIX=imp
+endif
+ifeq ($(OS_TARGET),netwlibc)
+EXEEXT=.nlm
+STATICLIBPREFIX=
+SHORTSUFFIX=nwl
+IMPORTLIBPREFIX=imp
+endif
+ifeq ($(OS_TARGET),macosclassic)
+BATCHEXT=
+EXEEXT=
+DEBUGSYMEXT=.xcoff
+SHORTSUFFIX=mac
+IMPORTLIBPREFIX=imp
+endif
+ifneq ($(findstring $(OS_TARGET),darwin iphonesim ios),)
+BATCHEXT=.sh
+EXEEXT=
+HASSHAREDLIB=1
+SHORTSUFFIX=dwn
+EXEDBGEXT=.dSYM
+endif
+ifeq ($(OS_TARGET),gba)
+EXEEXT=.gba
+SHAREDLIBEXT=.so
+SHORTSUFFIX=gba
+endif
+ifeq ($(OS_TARGET),symbian)
+SHAREDLIBEXT=.dll
+SHORTSUFFIX=symbian
+endif
+ifeq ($(OS_TARGET),NativeNT)
+SHAREDLIBEXT=.dll
+SHORTSUFFIX=nativent
+endif
+ifeq ($(OS_TARGET),wii)
+EXEEXT=.dol
+SHAREDLIBEXT=.so
+SHORTSUFFIX=wii
+endif
+ifeq ($(OS_TARGET),aix)
+BATCHEXT=.sh
+EXEEXT=
+SHAREDLIBEXT=.a
+SHORTSUFFIX=aix
+endif
+ifeq ($(OS_TARGET),java)
+OEXT=.class
+ASMEXT=.j
+SHAREDLIBEXT=.jar
+SHORTSUFFIX=java
+endif
+ifeq ($(CPU_TARGET),jvm)
+ifeq ($(OS_TARGET),android)
+OEXT=.class
+ASMEXT=.j
+SHAREDLIBEXT=.jar
+SHORTSUFFIX=android
+endif
+endif
+ifeq ($(OS_TARGET),msdos)
+STATICLIBPREFIX=
+STATICLIBEXT=.a
+SHORTSUFFIX=d16
+endif
+ifeq ($(OS_TARGET),msxdos)
+STATICLIBPREFIX=
+STATICLIBEXT=.a
+SHORTSUFFIX=msd
+endif
+ifeq ($(OS_TARGET),embedded)
+ifeq ($(CPU_TARGET),i8086)
+STATICLIBPREFIX=
+STATICLIBEXT=.a
+else
+EXEEXT=.bin
+endif
+ifeq ($(CPU_TARGET),z80)
+OEXT=.rel
+endif
+SHORTSUFFIX=emb
+endif
+ifeq ($(OS_TARGET),win16)
+STATICLIBPREFIX=
+STATICLIBEXT=.a
+SHAREDLIBEXT=.dll
+SHORTSUFFIX=w16
+endif
+ifeq ($(OS_TARGET),zxspectrum)
+OEXT=.rel
+endif
+ifeq ($(OS_TARGET),wasi)
+EXEEXT=.wasm
+endif
+ifneq ($(findstring $(OS_SOURCE),$(LIMIT83fs)),)
+FPCMADE=fpcmade.$(SHORTSUFFIX)
+ZIPSUFFIX=$(SHORTSUFFIX)
+ZIPCROSSPREFIX=
+ZIPSOURCESUFFIX=src
+ZIPEXAMPLESUFFIX=exm
+else
+FPCMADE=fpcmade.$(TARGETSUFFIX)
+ZIPSOURCESUFFIX=.source
+ZIPEXAMPLESUFFIX=.examples
+ifdef CROSSCOMPILE
+ZIPSUFFIX=.$(SOURCESUFFIX)
+ZIPCROSSPREFIX=$(TARGETSUFFIX)-
+else
+ZIPSUFFIX=.$(TARGETSUFFIX)
+ZIPCROSSPREFIX=
+endif
+endif
+ifndef ECHO
+ECHO:=$(strip $(wildcard $(addsuffix /gecho$(SRCEXEEXT),$(SEARCHPATH))))
+ifeq ($(ECHO),)
+ECHO:=$(strip $(wildcard $(addsuffix /echo$(SRCEXEEXT),$(SEARCHPATH))))
+ifeq ($(ECHO),)
+ECHO= __missing_command_ECHO
+else
+ECHO:=$(firstword $(ECHO))
+endif
+else
+ECHO:=$(firstword $(ECHO))
+endif
+endif
+export ECHO
+ifndef DATE
+DATE:=$(strip $(wildcard $(addsuffix /gdate$(SRCEXEEXT),$(SEARCHPATH))))
+ifeq ($(DATE),)
+DATE:=$(strip $(wildcard $(addsuffix /date$(SRCEXEEXT),$(SEARCHPATH))))
+ifeq ($(DATE),)
+DATE= __missing_command_DATE
+else
+DATE:=$(firstword $(DATE))
+endif
+else
+DATE:=$(firstword $(DATE))
+endif
+endif
+export DATE
+ifndef GINSTALL
+GINSTALL:=$(strip $(wildcard $(addsuffix /ginstall$(SRCEXEEXT),$(SEARCHPATH))))
+ifeq ($(GINSTALL),)
+GINSTALL:=$(strip $(wildcard $(addsuffix /install$(SRCEXEEXT),$(SEARCHPATH))))
+ifeq ($(GINSTALL),)
+GINSTALL= __missing_command_GINSTALL
+else
+GINSTALL:=$(firstword $(GINSTALL))
+endif
+else
+GINSTALL:=$(firstword $(GINSTALL))
+endif
+endif
+export GINSTALL
+ifndef CPPROG
+CPPROG:=$(strip $(wildcard $(addsuffix /cp$(SRCEXEEXT),$(SEARCHPATH))))
+ifeq ($(CPPROG),)
+CPPROG= __missing_command_CPPROG
+else
+CPPROG:=$(firstword $(CPPROG))
+endif
+endif
+export CPPROG
+ifndef RMPROG
+RMPROG:=$(strip $(wildcard $(addsuffix /rm$(SRCEXEEXT),$(SEARCHPATH))))
+ifeq ($(RMPROG),)
+RMPROG= __missing_command_RMPROG
+else
+RMPROG:=$(firstword $(RMPROG))
+endif
+endif
+export RMPROG
+ifndef MVPROG
+MVPROG:=$(strip $(wildcard $(addsuffix /mv$(SRCEXEEXT),$(SEARCHPATH))))
+ifeq ($(MVPROG),)
+MVPROG= __missing_command_MVPROG
+else
+MVPROG:=$(firstword $(MVPROG))
+endif
+endif
+export MVPROG
+ifndef MKDIRPROG
+MKDIRPROG:=$(strip $(wildcard $(addsuffix /gmkdir$(SRCEXEEXT),$(SEARCHPATH))))
+ifeq ($(MKDIRPROG),)
+MKDIRPROG:=$(strip $(wildcard $(addsuffix /mkdir$(SRCEXEEXT),$(SEARCHPATH))))
+ifeq ($(MKDIRPROG),)
+MKDIRPROG= __missing_command_MKDIRPROG
+else
+MKDIRPROG:=$(firstword $(MKDIRPROG))
+endif
+else
+MKDIRPROG:=$(firstword $(MKDIRPROG))
+endif
+endif
+export MKDIRPROG
+ifndef ECHOREDIR
+ifndef inUnix
+ECHOREDIR=echo
+else
+ECHOREDIR=$(ECHO)
+endif
+endif
+ifndef COPY
+COPY:=$(CPPROG) -fp
+endif
+ifndef COPYTREE
+COPYTREE:=$(CPPROG) -Rfp
+endif
+ifndef MKDIRTREE
+MKDIRTREE:=$(MKDIRPROG) -p
+endif
+ifndef MOVE
+MOVE:=$(MVPROG) -f
+endif
+ifndef DEL
+DEL:=$(RMPROG) -f
+endif
+ifndef DELTREE
+DELTREE:=$(RMPROG) -rf
+endif
+ifndef INSTALL
+ifdef inUnix
+INSTALL:=$(GINSTALL) -c -m 644
+else
+INSTALL:=$(COPY)
+endif
+endif
+ifndef INSTALLEXE
+ifdef inUnix
+INSTALLEXE:=$(GINSTALL) -c -m 755
+else
+INSTALLEXE:=$(COPY)
+endif
+endif
+ifndef MKDIR
+MKDIR:=$(GINSTALL) -m 755 -d
+endif
+export ECHOREDIR COPY COPYTREE MOVE DEL DELTREE INSTALL INSTALLEXE MKDIR
+ifndef PPUMOVE
+PPUMOVE:=$(strip $(wildcard $(addsuffix /ppumove$(SRCEXEEXT),$(SEARCHPATH))))
+ifeq ($(PPUMOVE),)
+PPUMOVE= __missing_command_PPUMOVE
+else
+PPUMOVE:=$(firstword $(PPUMOVE))
+endif
+endif
+export PPUMOVE
+ifndef FPCMAKE
+FPCMAKE:=$(strip $(wildcard $(addsuffix /fpcmake$(SRCEXEEXT),$(SEARCHPATH))))
+ifeq ($(FPCMAKE),)
+FPCMAKE= __missing_command_FPCMAKE
+else
+FPCMAKE:=$(firstword $(FPCMAKE))
+endif
+endif
+export FPCMAKE
+ifndef ZIPPROG
+ZIPPROG:=$(strip $(wildcard $(addsuffix /zip$(SRCEXEEXT),$(SEARCHPATH))))
+ifeq ($(ZIPPROG),)
+ZIPPROG= __missing_command_ZIPPROG
+else
+ZIPPROG:=$(firstword $(ZIPPROG))
+endif
+endif
+export ZIPPROG
+ifndef TARPROG
+TARPROG:=$(strip $(wildcard $(addsuffix /gtar$(SRCEXEEXT),$(SEARCHPATH))))
+ifeq ($(TARPROG),)
+TARPROG:=$(strip $(wildcard $(addsuffix /tar$(SRCEXEEXT),$(SEARCHPATH))))
+ifeq ($(TARPROG),)
+TARPROG= __missing_command_TARPROG
+else
+TARPROG:=$(firstword $(TARPROG))
+endif
+else
+TARPROG:=$(firstword $(TARPROG))
+endif
+endif
+export TARPROG
+ASNAME=$(BINUTILSPREFIX)as
+LDNAME=$(BINUTILSPREFIX)ld
+ARNAME=$(BINUTILSPREFIX)ar
+RCNAME=$(BINUTILSPREFIX)rc
+NASMNAME=$(BINUTILSPREFIX)nasm
+ifndef ASPROG
+ifdef CROSSBINDIR
+ASPROG=$(CROSSBINDIR)/$(ASNAME)$(SRCEXEEXT)
+else
+ASPROG=$(ASNAME)
+endif
+endif
+ifndef LDPROG
+ifdef CROSSBINDIR
+LDPROG=$(CROSSBINDIR)/$(LDNAME)$(SRCEXEEXT)
+else
+LDPROG=$(LDNAME)
+endif
+endif
+ifndef RCPROG
+ifdef CROSSBINDIR
+RCPROG=$(CROSSBINDIR)/$(RCNAME)$(SRCEXEEXT)
+else
+RCPROG=$(RCNAME)
+endif
+endif
+ifndef ARPROG
+ifdef CROSSBINDIR
+ARPROG=$(CROSSBINDIR)/$(ARNAME)$(SRCEXEEXT)
+else
+ARPROG=$(ARNAME)
+endif
+endif
+ifndef NASMPROG
+ifdef CROSSBINDIR
+NASMPROG=$(CROSSBINDIR)/$(NASMNAME)$(SRCEXEEXT)
+else
+NASMPROG=$(NASMNAME)
+endif
+endif
+AS=$(ASPROG)
+LD=$(LDPROG)
+RC=$(RCPROG)
+AR=$(ARPROG)
+NASM=$(NASMPROG)
+ifdef inUnix
+PPAS=./ppas$(SRCBATCHEXT)
+else
+PPAS=ppas$(SRCBATCHEXT)
+endif
+ifdef inUnix
+LDCONFIG=ldconfig
+else
+LDCONFIG=
+endif
+ifdef DATE
+DATESTR:=$(shell $(DATE) +%Y%m%d)
+else
+DATESTR=
+endif
+ZIPOPT=-9
+ZIPEXT=.zip
+ifeq ($(USETAR),bz2)
+TAROPT=vj
+TAREXT=.tar.bz2
+else
+TAROPT=vz
+TAREXT=.tar.gz
+endif
+override REQUIRE_PACKAGES=rtl fcl-base fpmkunit
+ifeq ($(FULL_TARGET),i386-linux)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),i386-go32v2)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),i386-win32)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),i386-os2)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),i386-freebsd)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),i386-beos)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),i386-haiku)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),i386-netbsd)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),i386-solaris)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),i386-netware)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),i386-openbsd)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),i386-wdosx)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),i386-darwin)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),i386-emx)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),i386-watcom)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),i386-netwlibc)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),i386-wince)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),i386-embedded)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),i386-symbian)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),i386-nativent)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),i386-iphonesim)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),i386-android)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),i386-aros)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),m68k-linux)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),m68k-netbsd)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),m68k-amiga)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),m68k-atari)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),m68k-palmos)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),m68k-macosclassic)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),m68k-embedded)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),m68k-sinclairql)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),powerpc-linux)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),powerpc-netbsd)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),powerpc-amiga)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),powerpc-macosclassic)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),powerpc-darwin)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),powerpc-morphos)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),powerpc-embedded)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),powerpc-wii)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),powerpc-aix)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),sparc-linux)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),sparc-netbsd)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),sparc-solaris)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),sparc-embedded)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),x86_64-linux)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),x86_64-freebsd)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),x86_64-haiku)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),x86_64-netbsd)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),x86_64-solaris)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),x86_64-openbsd)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),x86_64-darwin)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),x86_64-win64)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),x86_64-embedded)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),x86_64-iphonesim)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),x86_64-android)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),x86_64-aros)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),x86_64-dragonfly)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),arm-linux)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),arm-netbsd)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),arm-palmos)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),arm-wince)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),arm-gba)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),arm-nds)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),arm-embedded)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),arm-symbian)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),arm-android)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),arm-aros)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),arm-freertos)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),arm-ios)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),powerpc64-linux)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),powerpc64-darwin)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),powerpc64-embedded)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),powerpc64-aix)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),avr-embedded)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),armeb-linux)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),armeb-embedded)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),mips-linux)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),mipsel-linux)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),mipsel-embedded)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),mipsel-android)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),mips64el-linux)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),jvm-java)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),jvm-android)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),i8086-embedded)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),i8086-msdos)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),i8086-win16)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),aarch64-linux)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),aarch64-freebsd)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),aarch64-darwin)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),aarch64-win64)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),aarch64-android)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),aarch64-ios)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),wasm32-embedded)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),wasm32-wasi)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),sparc64-linux)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),riscv32-linux)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),riscv32-embedded)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),riscv64-linux)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),riscv64-embedded)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),xtensa-linux)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),xtensa-embedded)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),xtensa-freertos)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),z80-embedded)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),z80-zxspectrum)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),z80-msxdos)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifeq ($(FULL_TARGET),z80-amstradcpc)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_PASZLIB=1
+REQUIRE_PACKAGES_FCL-PROCESS=1
+REQUIRE_PACKAGES_HASH=1
+REQUIRE_PACKAGES_LIBTAR=1
+REQUIRE_PACKAGES_FPMKUNIT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+endif
+ifdef REQUIRE_PACKAGES_RTL
+PACKAGEDIR_RTL:=$(firstword $(subst /Makefile.fpc,,$(strip $(wildcard $(addsuffix /rtl/Makefile.fpc,$(PACKAGESDIR))))))
+ifneq ($(PACKAGEDIR_RTL),)
+ifneq ($(wildcard $(PACKAGEDIR_RTL)/units/$(TARGETSUFFIX)),)
+UNITDIR_RTL=$(PACKAGEDIR_RTL)/units/$(TARGETSUFFIX)
+else
+UNITDIR_RTL=$(PACKAGEDIR_RTL)
+endif
+ifneq ($(wildcard $(PACKAGEDIR_RTL)/units/$(SOURCESUFFIX)),)
+UNITDIR_FPMAKE_RTL=$(PACKAGEDIR_RTL)/units/$(SOURCESUFFIX)
+else
+ifneq ($(wildcard $(PACKAGEDIR_RTL)/units_bs/$(SOURCESUFFIX)),)
+UNITDIR_FPMAKE_RTL=$(PACKAGEDIR_RTL)/units_bs/$(SOURCESUFFIX)
+else
+UNITDIR_FPMAKE_RTL=$(PACKAGEDIR_RTL)
+endif
+endif
+ifdef CHECKDEPEND
+$(PACKAGEDIR_RTL)/$(OS_TARGET)/$(FPCMADE):
+	$(MAKE) -C $(PACKAGEDIR_RTL)/$(OS_TARGET) $(FPCMADE)
+override ALLDEPENDENCIES+=$(PACKAGEDIR_RTL)/$(OS_TARGET)/$(FPCMADE)
+endif
+else
+PACKAGEDIR_RTL=
+UNITDIR_RTL:=$(subst /Package.fpc,,$(strip $(wildcard $(addsuffix /rtl/Package.fpc,$(UNITSDIR)))))
+ifneq ($(UNITDIR_RTL),)
+UNITDIR_RTL:=$(firstword $(UNITDIR_RTL))
+else
+UNITDIR_RTL=
+endif
+endif
+ifdef UNITDIR_RTL
+override COMPILER_UNITDIR+=$(UNITDIR_RTL)
+endif
+ifdef UNITDIR_FPMAKE_RTL
+override COMPILER_FPMAKE_UNITDIR+=$(UNITDIR_FPMAKE_RTL)
+endif
+endif
+ifdef REQUIRE_PACKAGES_PASZLIB
+PACKAGEDIR_PASZLIB:=$(firstword $(subst /Makefile.fpc,,$(strip $(wildcard $(addsuffix /paszlib/Makefile.fpc,$(PACKAGESDIR))))))
+ifneq ($(PACKAGEDIR_PASZLIB),)
+ifneq ($(wildcard $(PACKAGEDIR_PASZLIB)/units/$(TARGETSUFFIX)),)
+UNITDIR_PASZLIB=$(PACKAGEDIR_PASZLIB)/units/$(TARGETSUFFIX)
+else
+UNITDIR_PASZLIB=$(PACKAGEDIR_PASZLIB)
+endif
+ifneq ($(wildcard $(PACKAGEDIR_PASZLIB)/units/$(SOURCESUFFIX)),)
+UNITDIR_FPMAKE_PASZLIB=$(PACKAGEDIR_PASZLIB)/units/$(SOURCESUFFIX)
+else
+ifneq ($(wildcard $(PACKAGEDIR_PASZLIB)/units_bs/$(SOURCESUFFIX)),)
+UNITDIR_FPMAKE_PASZLIB=$(PACKAGEDIR_PASZLIB)/units_bs/$(SOURCESUFFIX)
+else
+UNITDIR_FPMAKE_PASZLIB=$(PACKAGEDIR_PASZLIB)
+endif
+endif
+ifdef CHECKDEPEND
+$(PACKAGEDIR_PASZLIB)/$(FPCMADE):
+	$(MAKE) -C $(PACKAGEDIR_PASZLIB) $(FPCMADE)
+override ALLDEPENDENCIES+=$(PACKAGEDIR_PASZLIB)/$(FPCMADE)
+endif
+else
+PACKAGEDIR_PASZLIB=
+UNITDIR_PASZLIB:=$(subst /Package.fpc,,$(strip $(wildcard $(addsuffix /paszlib/Package.fpc,$(UNITSDIR)))))
+ifneq ($(UNITDIR_PASZLIB),)
+UNITDIR_PASZLIB:=$(firstword $(UNITDIR_PASZLIB))
+else
+UNITDIR_PASZLIB=
+endif
+endif
+ifdef UNITDIR_PASZLIB
+override COMPILER_UNITDIR+=$(UNITDIR_PASZLIB)
+endif
+ifdef UNITDIR_FPMAKE_PASZLIB
+override COMPILER_FPMAKE_UNITDIR+=$(UNITDIR_FPMAKE_PASZLIB)
+endif
+endif
+ifdef REQUIRE_PACKAGES_FCL-PROCESS
+PACKAGEDIR_FCL-PROCESS:=$(firstword $(subst /Makefile.fpc,,$(strip $(wildcard $(addsuffix /fcl-process/Makefile.fpc,$(PACKAGESDIR))))))
+ifneq ($(PACKAGEDIR_FCL-PROCESS),)
+ifneq ($(wildcard $(PACKAGEDIR_FCL-PROCESS)/units/$(TARGETSUFFIX)),)
+UNITDIR_FCL-PROCESS=$(PACKAGEDIR_FCL-PROCESS)/units/$(TARGETSUFFIX)
+else
+UNITDIR_FCL-PROCESS=$(PACKAGEDIR_FCL-PROCESS)
+endif
+ifneq ($(wildcard $(PACKAGEDIR_FCL-PROCESS)/units/$(SOURCESUFFIX)),)
+UNITDIR_FPMAKE_FCL-PROCESS=$(PACKAGEDIR_FCL-PROCESS)/units/$(SOURCESUFFIX)
+else
+ifneq ($(wildcard $(PACKAGEDIR_FCL-PROCESS)/units_bs/$(SOURCESUFFIX)),)
+UNITDIR_FPMAKE_FCL-PROCESS=$(PACKAGEDIR_FCL-PROCESS)/units_bs/$(SOURCESUFFIX)
+else
+UNITDIR_FPMAKE_FCL-PROCESS=$(PACKAGEDIR_FCL-PROCESS)
+endif
+endif
+ifdef CHECKDEPEND
+$(PACKAGEDIR_FCL-PROCESS)/$(FPCMADE):
+	$(MAKE) -C $(PACKAGEDIR_FCL-PROCESS) $(FPCMADE)
+override ALLDEPENDENCIES+=$(PACKAGEDIR_FCL-PROCESS)/$(FPCMADE)
+endif
+else
+PACKAGEDIR_FCL-PROCESS=
+UNITDIR_FCL-PROCESS:=$(subst /Package.fpc,,$(strip $(wildcard $(addsuffix /fcl-process/Package.fpc,$(UNITSDIR)))))
+ifneq ($(UNITDIR_FCL-PROCESS),)
+UNITDIR_FCL-PROCESS:=$(firstword $(UNITDIR_FCL-PROCESS))
+else
+UNITDIR_FCL-PROCESS=
+endif
+endif
+ifdef UNITDIR_FCL-PROCESS
+override COMPILER_UNITDIR+=$(UNITDIR_FCL-PROCESS)
+endif
+ifdef UNITDIR_FPMAKE_FCL-PROCESS
+override COMPILER_FPMAKE_UNITDIR+=$(UNITDIR_FPMAKE_FCL-PROCESS)
+endif
+endif
+ifdef REQUIRE_PACKAGES_HASH
+PACKAGEDIR_HASH:=$(firstword $(subst /Makefile.fpc,,$(strip $(wildcard $(addsuffix /hash/Makefile.fpc,$(PACKAGESDIR))))))
+ifneq ($(PACKAGEDIR_HASH),)
+ifneq ($(wildcard $(PACKAGEDIR_HASH)/units/$(TARGETSUFFIX)),)
+UNITDIR_HASH=$(PACKAGEDIR_HASH)/units/$(TARGETSUFFIX)
+else
+UNITDIR_HASH=$(PACKAGEDIR_HASH)
+endif
+ifneq ($(wildcard $(PACKAGEDIR_HASH)/units/$(SOURCESUFFIX)),)
+UNITDIR_FPMAKE_HASH=$(PACKAGEDIR_HASH)/units/$(SOURCESUFFIX)
+else
+ifneq ($(wildcard $(PACKAGEDIR_HASH)/units_bs/$(SOURCESUFFIX)),)
+UNITDIR_FPMAKE_HASH=$(PACKAGEDIR_HASH)/units_bs/$(SOURCESUFFIX)
+else
+UNITDIR_FPMAKE_HASH=$(PACKAGEDIR_HASH)
+endif
+endif
+ifdef CHECKDEPEND
+$(PACKAGEDIR_HASH)/$(FPCMADE):
+	$(MAKE) -C $(PACKAGEDIR_HASH) $(FPCMADE)
+override ALLDEPENDENCIES+=$(PACKAGEDIR_HASH)/$(FPCMADE)
+endif
+else
+PACKAGEDIR_HASH=
+UNITDIR_HASH:=$(subst /Package.fpc,,$(strip $(wildcard $(addsuffix /hash/Package.fpc,$(UNITSDIR)))))
+ifneq ($(UNITDIR_HASH),)
+UNITDIR_HASH:=$(firstword $(UNITDIR_HASH))
+else
+UNITDIR_HASH=
+endif
+endif
+ifdef UNITDIR_HASH
+override COMPILER_UNITDIR+=$(UNITDIR_HASH)
+endif
+ifdef UNITDIR_FPMAKE_HASH
+override COMPILER_FPMAKE_UNITDIR+=$(UNITDIR_FPMAKE_HASH)
+endif
+endif
+ifdef REQUIRE_PACKAGES_LIBTAR
+PACKAGEDIR_LIBTAR:=$(firstword $(subst /Makefile.fpc,,$(strip $(wildcard $(addsuffix /libtar/Makefile.fpc,$(PACKAGESDIR))))))
+ifneq ($(PACKAGEDIR_LIBTAR),)
+ifneq ($(wildcard $(PACKAGEDIR_LIBTAR)/units/$(TARGETSUFFIX)),)
+UNITDIR_LIBTAR=$(PACKAGEDIR_LIBTAR)/units/$(TARGETSUFFIX)
+else
+UNITDIR_LIBTAR=$(PACKAGEDIR_LIBTAR)
+endif
+ifneq ($(wildcard $(PACKAGEDIR_LIBTAR)/units/$(SOURCESUFFIX)),)
+UNITDIR_FPMAKE_LIBTAR=$(PACKAGEDIR_LIBTAR)/units/$(SOURCESUFFIX)
+else
+ifneq ($(wildcard $(PACKAGEDIR_LIBTAR)/units_bs/$(SOURCESUFFIX)),)
+UNITDIR_FPMAKE_LIBTAR=$(PACKAGEDIR_LIBTAR)/units_bs/$(SOURCESUFFIX)
+else
+UNITDIR_FPMAKE_LIBTAR=$(PACKAGEDIR_LIBTAR)
+endif
+endif
+ifdef CHECKDEPEND
+$(PACKAGEDIR_LIBTAR)/$(FPCMADE):
+	$(MAKE) -C $(PACKAGEDIR_LIBTAR) $(FPCMADE)
+override ALLDEPENDENCIES+=$(PACKAGEDIR_LIBTAR)/$(FPCMADE)
+endif
+else
+PACKAGEDIR_LIBTAR=
+UNITDIR_LIBTAR:=$(subst /Package.fpc,,$(strip $(wildcard $(addsuffix /libtar/Package.fpc,$(UNITSDIR)))))
+ifneq ($(UNITDIR_LIBTAR),)
+UNITDIR_LIBTAR:=$(firstword $(UNITDIR_LIBTAR))
+else
+UNITDIR_LIBTAR=
+endif
+endif
+ifdef UNITDIR_LIBTAR
+override COMPILER_UNITDIR+=$(UNITDIR_LIBTAR)
+endif
+ifdef UNITDIR_FPMAKE_LIBTAR
+override COMPILER_FPMAKE_UNITDIR+=$(UNITDIR_FPMAKE_LIBTAR)
+endif
+endif
+ifdef REQUIRE_PACKAGES_FPMKUNIT
+PACKAGEDIR_FPMKUNIT:=$(firstword $(subst /Makefile.fpc,,$(strip $(wildcard $(addsuffix /fpmkunit/Makefile.fpc,$(PACKAGESDIR))))))
+ifneq ($(PACKAGEDIR_FPMKUNIT),)
+ifneq ($(wildcard $(PACKAGEDIR_FPMKUNIT)/units/$(TARGETSUFFIX)),)
+UNITDIR_FPMKUNIT=$(PACKAGEDIR_FPMKUNIT)/units/$(TARGETSUFFIX)
+else
+UNITDIR_FPMKUNIT=$(PACKAGEDIR_FPMKUNIT)
+endif
+ifneq ($(wildcard $(PACKAGEDIR_FPMKUNIT)/units/$(SOURCESUFFIX)),)
+UNITDIR_FPMAKE_FPMKUNIT=$(PACKAGEDIR_FPMKUNIT)/units/$(SOURCESUFFIX)
+else
+ifneq ($(wildcard $(PACKAGEDIR_FPMKUNIT)/units_bs/$(SOURCESUFFIX)),)
+UNITDIR_FPMAKE_FPMKUNIT=$(PACKAGEDIR_FPMKUNIT)/units_bs/$(SOURCESUFFIX)
+else
+UNITDIR_FPMAKE_FPMKUNIT=$(PACKAGEDIR_FPMKUNIT)
+endif
+endif
+ifdef CHECKDEPEND
+$(PACKAGEDIR_FPMKUNIT)/$(FPCMADE):
+	$(MAKE) -C $(PACKAGEDIR_FPMKUNIT) $(FPCMADE)
+override ALLDEPENDENCIES+=$(PACKAGEDIR_FPMKUNIT)/$(FPCMADE)
+endif
+else
+PACKAGEDIR_FPMKUNIT=
+UNITDIR_FPMKUNIT:=$(subst /Package.fpc,,$(strip $(wildcard $(addsuffix /fpmkunit/Package.fpc,$(UNITSDIR)))))
+ifneq ($(UNITDIR_FPMKUNIT),)
+UNITDIR_FPMKUNIT:=$(firstword $(UNITDIR_FPMKUNIT))
+else
+UNITDIR_FPMKUNIT=
+endif
+endif
+ifdef UNITDIR_FPMKUNIT
+override COMPILER_UNITDIR+=$(UNITDIR_FPMKUNIT)
+endif
+ifdef UNITDIR_FPMAKE_FPMKUNIT
+override COMPILER_FPMAKE_UNITDIR+=$(UNITDIR_FPMAKE_FPMKUNIT)
+endif
+endif
+ifdef REQUIRE_PACKAGES_FCL-BASE
+PACKAGEDIR_FCL-BASE:=$(firstword $(subst /Makefile.fpc,,$(strip $(wildcard $(addsuffix /fcl-base/Makefile.fpc,$(PACKAGESDIR))))))
+ifneq ($(PACKAGEDIR_FCL-BASE),)
+ifneq ($(wildcard $(PACKAGEDIR_FCL-BASE)/units/$(TARGETSUFFIX)),)
+UNITDIR_FCL-BASE=$(PACKAGEDIR_FCL-BASE)/units/$(TARGETSUFFIX)
+else
+UNITDIR_FCL-BASE=$(PACKAGEDIR_FCL-BASE)
+endif
+ifneq ($(wildcard $(PACKAGEDIR_FCL-BASE)/units/$(SOURCESUFFIX)),)
+UNITDIR_FPMAKE_FCL-BASE=$(PACKAGEDIR_FCL-BASE)/units/$(SOURCESUFFIX)
+else
+ifneq ($(wildcard $(PACKAGEDIR_FCL-BASE)/units_bs/$(SOURCESUFFIX)),)
+UNITDIR_FPMAKE_FCL-BASE=$(PACKAGEDIR_FCL-BASE)/units_bs/$(SOURCESUFFIX)
+else
+UNITDIR_FPMAKE_FCL-BASE=$(PACKAGEDIR_FCL-BASE)
+endif
+endif
+ifdef CHECKDEPEND
+$(PACKAGEDIR_FCL-BASE)/$(FPCMADE):
+	$(MAKE) -C $(PACKAGEDIR_FCL-BASE) $(FPCMADE)
+override ALLDEPENDENCIES+=$(PACKAGEDIR_FCL-BASE)/$(FPCMADE)
+endif
+else
+PACKAGEDIR_FCL-BASE=
+UNITDIR_FCL-BASE:=$(subst /Package.fpc,,$(strip $(wildcard $(addsuffix /fcl-base/Package.fpc,$(UNITSDIR)))))
+ifneq ($(UNITDIR_FCL-BASE),)
+UNITDIR_FCL-BASE:=$(firstword $(UNITDIR_FCL-BASE))
+else
+UNITDIR_FCL-BASE=
+endif
+endif
+ifdef UNITDIR_FCL-BASE
+override COMPILER_UNITDIR+=$(UNITDIR_FCL-BASE)
+endif
+ifdef UNITDIR_FPMAKE_FCL-BASE
+override COMPILER_FPMAKE_UNITDIR+=$(UNITDIR_FPMAKE_FCL-BASE)
+endif
+endif
+ifndef NOCPUDEF
+override FPCOPTDEF=$(ARCH)
+endif
+ifneq ($(OS_TARGET),$(OS_SOURCE))
+override FPCOPT+=-T$(OS_TARGET)
+endif
+ifneq ($(CPU_TARGET),$(CPU_SOURCE))
+override FPCOPT+=-P$(ARCH)
+endif
+ifeq ($(OS_SOURCE),openbsd)
+override FPCOPT+=-FD$(NEW_BINUTILS_PATH)
+override FPCMAKEOPT+=-FD$(NEW_BINUTILS_PATH)
+override FPMAKE_BUILD_OPT+=-FD$(NEW_BINUTILS_PATH)
+endif
+ifndef CROSSBOOTSTRAP
+ifneq ($(BINUTILSPREFIX),)
+override FPCOPT+=-XP$(BINUTILSPREFIX)
+ifneq ($(RLINKPATH),)
+override FPCOPT+=-Xr$(RLINKPATH)
+endif
+endif
+endif
+ifndef CROSSCOMPILE
+ifneq ($(BINUTILSPREFIX),)
+override FPCMAKEOPT+=-XP$(BINUTILSPREFIX)
+override FPMAKE_BUILD_OPT+=-XP$(BINUTILSPREFIX)
+endif
+endif
+ifdef UNITDIR
+override FPCOPT+=$(addprefix -Fu,$(UNITDIR))
+endif
+ifdef LIBDIR
+override FPCOPT+=$(addprefix -Fl,$(LIBDIR))
+endif
+ifdef OBJDIR
+override FPCOPT+=$(addprefix -Fo,$(OBJDIR))
+endif
+ifdef INCDIR
+override FPCOPT+=$(addprefix -Fi,$(INCDIR))
+endif
+ifdef LINKSMART
+override FPCOPT+=-XX
+endif
+ifdef CREATESMART
+override FPCOPT+=-CX
+endif
+ifdef DEBUG
+override FPCOPT+=-gl
+override FPCOPTDEF+=DEBUG
+endif
+ifdef RELEASE
+FPCCPUOPT:=-O2
+override FPCOPT+=-Ur -Xs $(FPCCPUOPT) -n
+override FPCOPTDEF+=RELEASE
+endif
+ifdef STRIP
+override FPCOPT+=-Xs
+endif
+ifdef OPTIMIZE
+override FPCOPT+=-O2
+endif
+ifdef VERBOSE
+override FPCOPT+=-vwni
+endif
+ifdef COMPILER_OPTIONS
+override FPCOPT+=$(COMPILER_OPTIONS)
+endif
+ifdef COMPILER_UNITDIR
+override FPCOPT+=$(addprefix -Fu,$(COMPILER_UNITDIR))
+endif
+ifdef COMPILER_LIBRARYDIR
+override FPCOPT+=$(addprefix -Fl,$(COMPILER_LIBRARYDIR))
+endif
+ifdef COMPILER_OBJECTDIR
+override FPCOPT+=$(addprefix -Fo,$(COMPILER_OBJECTDIR))
+endif
+ifdef COMPILER_INCLUDEDIR
+override FPCOPT+=$(addprefix -Fi,$(COMPILER_INCLUDEDIR))
+endif
+ifdef CROSSBINDIR
+override FPCOPT+=-FD$(CROSSBINDIR)
+endif
+ifdef COMPILER_TARGETDIR
+override FPCOPT+=-FE$(COMPILER_TARGETDIR)
+ifeq ($(COMPILER_TARGETDIR),.)
+override TARGETDIRPREFIX=
+else
+override TARGETDIRPREFIX=$(COMPILER_TARGETDIR)/
+endif
+endif
+ifdef COMPILER_UNITTARGETDIR
+override FPCOPT+=-FU$(COMPILER_UNITTARGETDIR)
+ifeq ($(COMPILER_UNITTARGETDIR),.)
+override UNITTARGETDIRPREFIX=
+else
+override UNITTARGETDIRPREFIX=$(COMPILER_UNITTARGETDIR)/
+endif
+else
+ifdef COMPILER_TARGETDIR
+override COMPILER_UNITTARGETDIR=$(COMPILER_TARGETDIR)
+override UNITTARGETDIRPREFIX=$(TARGETDIRPREFIX)
+endif
+endif
+ifdef SYSROOTPATH
+override FPCOPT+=-XR$(SYSROOTPATH)
+else
+ifeq ($(OS_TARGET),$(OS_SOURCE))
+ifneq ($(findstring $(OS_TARGET),darwin),)
+ifneq ($(findstring $(CPU_TARGET),aarch64),)
+ifneq ($(wildcard /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk),)
+override FPCOPT+=-XR/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk
+endif
+endif
+endif
+endif
+endif
+ifdef CREATESHARED
+override FPCOPT+=-Cg
+endif
+ifneq ($(findstring $(OS_TARGET),dragonfly freebsd openbsd netbsd linux solaris),)
+ifneq ($(findstring $(CPU_TARGET),x86_64 mips mipsel riscv64),)
+override FPCOPT+=-Cg
+endif
+endif
+ifdef LINKSHARED
+endif
+ifdef GCCLIBDIR
+override FPCOPT+=-Fl$(GCCLIBDIR)
+ifdef FPCMAKEGCCLIBDIR
+override FPCMAKEOPT+=-Fl$(FPCMAKEGCCLIBDIR)
+else
+override FPCMAKEOPT+=-Fl$(GCCLIBDIR)
+endif
+endif
+ifdef OTHERLIBDIR
+override FPCOPT+=$(addprefix -Fl,$(OTHERLIBDIR))
+endif
+ifdef OPT
+override FPCOPT+=$(OPT)
+endif
+ifdef FPMAKEBUILDOPT
+override FPMAKE_BUILD_OPT+=$(FPMAKEBUILDOPT)
+endif
+ifdef FPCOPTDEF
+override FPCOPT+=$(addprefix -d,$(FPCOPTDEF))
+endif
+ifdef CFGFILE
+override FPCOPT+=@$(CFGFILE)
+endif
+ifdef USEENV
+override FPCEXTCMD:=$(FPCOPT)
+override FPCOPT:=!FPCEXTCMD
+export FPCEXTCMD
+endif
+override AFULL_TARGET=$(CPU_TARGET)-$(OS_TARGET)
+override AFULL_SOURCE=$(CPU_SOURCE)-$(OS_SOURCE)
+ifneq ($(AFULL_TARGET),$(AFULL_SOURCE))
+override ACROSSCOMPILE=1
+endif
+ifdef ACROSSCOMPILE
+override FPCOPT+=$(CROSSOPT)
+endif
+override COMPILER:=$(strip $(FPC) $(FPCOPT))
+ifneq (,$(findstring -sh ,$(COMPILER)))
+UseEXECPPAS=1
+endif
+ifneq (,$(findstring -s ,$(COMPILER)))
+ifeq ($(FULL_SOURCE),$(FULL_TARGET))
+UseEXECPPAS=1
+endif
+endif
+ifneq ($(UseEXECPPAS),1)
+EXECPPAS=
+else
+ifdef RUNBATCH
+EXECPPAS:=@$(RUNBATCH) $(PPAS)
+else
+EXECPPAS:=@$(PPAS)
+endif
+endif
+ifdef TARGET_RSTS
+override RSTFILES=$(addsuffix $(RSTEXT),$(TARGET_RSTS))
+override CLEANRSTFILES+=$(RSTFILES)
+endif
+.PHONY: fpc_install fpc_sourceinstall fpc_exampleinstall
+ifdef INSTALL_UNITS
+override INSTALLPPUFILES+=$(addsuffix $(PPUEXT),$(INSTALL_UNITS))
+endif
+ifdef INSTALL_BUILDUNIT
+override INSTALLPPUFILES:=$(filter-out $(INSTALL_BUILDUNIT)$(PPUEXT),$(INSTALLPPUFILES))
+endif
+ifdef INSTALLPPUFILES
+ifneq ($(IMPORTLIBPREFIX)-$(STATICLIBEXT),$(STATICLIBPREFIX)-$(STATICLIBEXT))
+override INSTALLPPULINKFILES:=$(subst $(PPUEXT),$(OEXT),$(INSTALLPPUFILES)) $(subst $(PPUEXT),$(LTOEXT),$(INSTALLPPUFILES)) $(addprefix $(STATICLIBPREFIX),$(subst $(PPUEXT),$(STATICLIBEXT),$(INSTALLPPUFILES))) $(addprefix $(IMPORTLIBPREFIX),$(subst $(PPUEXT),$(STATICLIBEXT),$(INSTALLPPUFILES)))
+else
+override INSTALLPPULINKFILES:=$(subst $(PPUEXT),$(OEXT),$(INSTALLPPUFILES)) $(subst $(PPUEXT),$(LTOEXT),$(INSTALLPPUFILES)) $(addprefix $(STATICLIBPREFIX),$(subst $(PPUEXT),$(STATICLIBEXT),$(INSTALLPPUFILES)))
+endif
+ifneq ($(UNITTARGETDIRPREFIX),)
+override INSTALLPPUFILENAMES:=$(notdir $(INSTALLPPUFILES))
+override INSTALLPPULINKFILENAMES:=$(notdir $(INSTALLPPULINKFILES))
+override INSTALLPPUFILES=$(addprefix $(UNITTARGETDIRPREFIX),$(INSTALLPPUFILENAMES))
+override INSTALLPPULINKFILES=$(wildcard $(addprefix $(UNITTARGETDIRPREFIX),$(INSTALLPPULINKFILENAMES)))
+endif
+override INSTALL_CREATEPACKAGEFPC=1
+endif
+ifdef INSTALLEXEFILES
+ifneq ($(TARGETDIRPREFIX),)
+override INSTALLEXEFILES:=$(addprefix $(TARGETDIRPREFIX),$(notdir $(INSTALLEXEFILES)))
+endif
+endif
+fpc_install: all $(INSTALLTARGET)
+ifdef INSTALLEXEFILES
+	$(MKDIR) $(INSTALL_BINDIR)
+	$(INSTALLEXE) $(INSTALLEXEFILES) $(INSTALL_BINDIR)
+endif
+ifdef INSTALL_CREATEPACKAGEFPC
+ifdef FPCMAKE
+ifdef PACKAGE_VERSION
+ifneq ($(wildcard Makefile.fpc),)
+	$(FPCMAKE) -p -T$(CPU_TARGET)-$(OS_TARGET) Makefile.fpc
+	$(MKDIR) $(INSTALL_UNITDIR)
+	$(INSTALL) Package.fpc $(INSTALL_UNITDIR)
+endif
+endif
+endif
+endif
+ifdef INSTALLPPUFILES
+	$(MKDIR) $(INSTALL_UNITDIR)
+	$(INSTALL) $(INSTALLPPUFILES) $(INSTALL_UNITDIR)
+ifneq ($(INSTALLPPULINKFILES),)
+	$(INSTALL) $(INSTALLPPULINKFILES) $(INSTALL_UNITDIR)
+endif
+ifneq ($(wildcard $(LIB_FULLNAME)),)
+	$(MKDIR) $(INSTALL_LIBDIR)
+	$(INSTALL) $(LIB_FULLNAME) $(INSTALL_LIBDIR)
+ifdef inUnix
+	ln -sf $(LIB_FULLNAME) $(INSTALL_LIBDIR)/$(LIB_NAME)
+endif
+endif
+endif
+ifdef INSTALL_FILES
+	$(MKDIR) $(INSTALL_DATADIR)
+	$(INSTALL) $(INSTALL_FILES) $(INSTALL_DATADIR)
+endif
+fpc_sourceinstall: distclean
+	$(MKDIR) $(INSTALL_SOURCEDIR)
+	$(COPYTREE) $(BASEDIR)/* $(INSTALL_SOURCEDIR)
+fpc_exampleinstall: $(EXAMPLEINSTALLTARGET) $(addsuffix _distclean,$(TARGET_EXAMPLEDIRS))
+ifdef HASEXAMPLES
+	$(MKDIR) $(INSTALL_EXAMPLEDIR)
+endif
+ifdef EXAMPLESOURCEFILES
+	$(COPY) $(EXAMPLESOURCEFILES) $(INSTALL_EXAMPLEDIR)
+endif
+ifdef TARGET_EXAMPLEDIRS
+	$(COPYTREE) $(addsuffix /*,$(TARGET_EXAMPLEDIRS)) $(INSTALL_EXAMPLEDIR)
+endif
+.PHONY: fpc_distinstall
+fpc_distinstall: install exampleinstall
+.PHONY: fpc_zipinstall fpc_zipsourceinstall fpc_zipexampleinstall
+ifndef PACKDIR
+ifndef inUnix
+PACKDIR=$(BASEDIR)/../fpc-pack
+else
+PACKDIR=/tmp/fpc-pack
+endif
+endif
+ifndef ZIPNAME
+ifdef DIST_ZIPNAME
+ZIPNAME=$(DIST_ZIPNAME)
+else
+ZIPNAME=$(PACKAGE_NAME)
+endif
+endif
+ifndef FULLZIPNAME
+FULLZIPNAME=$(ZIPCROSSPREFIX)$(ZIPPREFIX)$(ZIPNAME)$(ZIPSUFFIX)
+endif
+ifndef ZIPTARGET
+ifdef DIST_ZIPTARGET
+ZIPTARGET=DIST_ZIPTARGET
+else
+ZIPTARGET=install
+endif
+endif
+ifndef USEZIP
+ifdef inUnix
+USETAR=1
+endif
+endif
+ifndef inUnix
+USEZIPWRAPPER=1
+endif
+ifdef USEZIPWRAPPER
+ZIPPATHSEP=$(PATHSEP)
+ZIPWRAPPER=$(subst /,$(PATHSEP),$(DIST_DESTDIR)/fpczip$(SRCBATCHEXT))
+else
+ZIPPATHSEP=/
+endif
+ZIPCMD_CDPACK:=cd $(subst /,$(ZIPPATHSEP),$(PACKDIR))
+ZIPCMD_CDBASE:=cd $(subst /,$(ZIPPATHSEP),$(BASEDIR))
+ifdef USETAR
+ZIPDESTFILE:=$(DIST_DESTDIR)/$(FULLZIPNAME)$(TAREXT)
+ZIPCMD_ZIP:=$(TARPROG) c$(TAROPT)f $(ZIPDESTFILE) *
+else
+ZIPDESTFILE:=$(DIST_DESTDIR)/$(FULLZIPNAME)$(ZIPEXT)
+ZIPCMD_ZIP:=$(subst /,$(ZIPPATHSEP),$(ZIPPROG)) -Dr $(ZIPOPT) $(ZIPDESTFILE) *
+endif
+fpc_zipinstall:
+	$(MAKE) $(ZIPTARGET) INSTALL_PREFIX=$(PACKDIR) ZIPINSTALL=1
+	$(MKDIR) $(DIST_DESTDIR)
+	$(DEL) $(ZIPDESTFILE)
+ifdef USEZIPWRAPPER
+ifneq ($(ECHOREDIR),echo)
+	$(ECHOREDIR) -e "$(subst \,\\,$(ZIPCMD_CDPACK))" > $(ZIPWRAPPER)
+	$(ECHOREDIR) -e "$(subst \,\\,$(ZIPCMD_ZIP))" >> $(ZIPWRAPPER)
+	$(ECHOREDIR) -e "$(subst \,\\,$(ZIPCMD_CDBASE))" >> $(ZIPWRAPPER)
+else
+	echo $(ZIPCMD_CDPACK) > $(ZIPWRAPPER)
+	echo $(ZIPCMD_ZIP) >> $(ZIPWRAPPER)
+	echo $(ZIPCMD_CDBASE) >> $(ZIPWRAPPER)
+endif
+ifdef inUnix
+	/bin/sh $(ZIPWRAPPER)
+else
+ifdef RUNBATCH
+	$(RUNBATCH) $(ZIPWRAPPER)
+else
+	$(ZIPWRAPPER)
+endif
+endif
+	$(DEL) $(ZIPWRAPPER)
+else
+	$(ZIPCMD_CDPACK) ; $(ZIPCMD_ZIP) ; $(ZIPCMD_CDBASE)
+endif
+	$(DELTREE) $(PACKDIR)
+fpc_zipsourceinstall:
+	$(MAKE) fpc_zipinstall ZIPTARGET=sourceinstall ZIPSUFFIX=$(ZIPSOURCESUFFIX)
+fpc_zipexampleinstall:
+ifdef HASEXAMPLES
+	$(MAKE) fpc_zipinstall ZIPTARGET=exampleinstall ZIPSUFFIX=$(ZIPEXAMPLESUFFIX)
+endif
+fpc_zipdistinstall:
+	$(MAKE) fpc_zipinstall ZIPTARGET=distinstall
+.PHONY: fpc_clean fpc_cleanall fpc_distclean
+ifdef EXEFILES
+override CLEANEXEFILES:=$(addprefix $(TARGETDIRPREFIX),$(CLEANEXEFILES))
+override CLEANEXEDBGFILES:=$(addprefix $(TARGETDIRPREFIX),$(CLEANEXEDBGFILES))
+endif
+ifdef CLEAN_PROGRAMS
+override CLEANEXEFILES+=$(addprefix $(TARGETDIRPREFIX),$(addsuffix $(EXEEXT), $(CLEAN_PROGRAMS)))
+override CLEANEXEDBGFILES+=$(addprefix $(TARGETDIRPREFIX),$(addsuffix $(EXEDBGEXT), $(CLEAN_PROGRAMS)))
+endif
+ifdef CLEAN_UNITS
+override CLEANPPUFILES+=$(addsuffix $(PPUEXT),$(CLEAN_UNITS))
+endif
+ifdef CLEANPPUFILES
+override CLEANPPULINKFILES:=$(subst $(PPUEXT),$(OEXT),$(CLEANPPUFILES)) $(subst $(PPUEXT),$(LTOEXT),$(CLEANPPUFILES)) $(addprefix $(STATICLIBPREFIX),$(subst $(PPUEXT),$(STATICLIBEXT),$(CLEANPPUFILES))) $(addprefix $(IMPORTLIBPREFIX),$(subst $(PPUEXT),$(STATICLIBEXT),$(CLEANPPUFILES)))
+ifdef DEBUGSYMEXT
+override CLEANPPULINKFILES+=$(subst $(PPUEXT),$(DEBUGSYMEXT),$(CLEANPPUFILES))
+endif
+override CLEANPPUFILENAMES:=$(CLEANPPUFILES)
+override CLEANPPUFILES=$(addprefix $(UNITTARGETDIRPREFIX),$(CLEANPPUFILENAMES))
+override CLEANPPULINKFILENAMES:=$(CLEANPPULINKFILES)
+override CLEANPPULINKFILES=$(wildcard $(addprefix $(UNITTARGETDIRPREFIX),$(CLEANPPULINKFILENAMES)))
+endif
+fpc_clean: $(CLEANTARGET)
+ifdef CLEANEXEFILES
+	-$(DEL) $(CLEANEXEFILES)
+endif
+ifdef CLEANEXEDBGFILES
+	-$(DELTREE) $(CLEANEXEDBGFILES)
+endif
+ifdef CLEANPPUFILES
+	-$(DEL) $(CLEANPPUFILES)
+endif
+ifneq ($(CLEANPPULINKFILES),)
+	-$(DEL) $(CLEANPPULINKFILES)
+endif
+ifdef CLEANRSTFILES
+	-$(DEL) $(addprefix $(UNITTARGETDIRPREFIX),$(CLEANRSTFILES))
+endif
+ifdef CLEAN_FILES
+	-$(DEL) $(CLEAN_FILES)
+endif
+ifdef LIB_NAME
+	-$(DEL) $(LIB_NAME) $(LIB_FULLNAME)
+endif
+	-$(DEL) $(FPCMADE) *$(FULL_TARGET).fpm Package.fpc *$(ASMEXT)
+	-$(DEL) $(FPCEXTFILE) $(REDIRFILE) script*.res link*.res *_script.res *_link.res
+	-$(DEL) $(PPAS) *_ppas$(BATCHEXT) ppas$(BATCHEXT) ppaslink$(BATCHEXT)
+fpc_cleanall: $(CLEANTARGET)
+ifdef CLEANEXEFILES
+	-$(DEL) $(CLEANEXEFILES)
+endif
+ifdef COMPILER_UNITTARGETDIR
+ifdef CLEANPPUFILES
+	-$(DEL) $(CLEANPPUFILES)
+endif
+ifneq ($(CLEANPPULINKFILES),)
+	-$(DEL) $(CLEANPPULINKFILES)
+endif
+ifdef CLEANRSTFILES
+	-$(DEL) $(addprefix $(UNITTARGETDIRPREFIX),$(CLEANRSTFILES))
+endif
+endif
+ifdef CLEAN_FILES
+	-$(DEL) $(CLEAN_FILES)
+endif
+	-$(DELTREE) units
+	-$(DELTREE) bin
+	-$(DEL) *$(OEXT) *$(LTOEXT) *$(PPUEXT) *$(RSTEXT) *$(ASMEXT) *$(STATICLIBEXT) *$(SHAREDLIBEXT) *$(PPLEXT)
+ifneq ($(PPUEXT),.ppu)
+	-$(DEL) *.o *.ppu *.a
+endif
+	-$(DELTREE) *$(SMARTEXT)
+	-$(DEL) fpcmade.* Package.fpc *.fpm
+	-$(DEL) $(FPCEXTFILE) $(REDIRFILE) script*.res link*.res *_script.res *_link.res
+	-$(DEL) $(PPAS) *_ppas$(BATCHEXT) ppas$(BATCHEXT) ppaslink$(BATCHEXT)
+ifdef AOUTEXT
+	-$(DEL) *$(AOUTEXT)
+endif
+ifdef DEBUGSYMEXT
+	-$(DEL) *$(DEBUGSYMEXT)
+endif
+ifdef LOCALFPMAKEBIN
+	-$(DEL) $(LOCALFPMAKEBIN)
+	-$(DEL) $(FPMAKEBINOBJ)
+endif
+fpc_distclean: cleanall
+.PHONY: fpc_baseinfo
+override INFORULES+=fpc_baseinfo
+fpc_baseinfo:
+	@$(ECHO)
+	@$(ECHO)  == Package info ==
+	@$(ECHO)  Package Name..... $(PACKAGE_NAME)
+	@$(ECHO)  Package Version.. $(PACKAGE_VERSION)
+	@$(ECHO)
+	@$(ECHO)  == Configuration info ==
+	@$(ECHO)
+	@$(ECHO)  FPC.......... $(FPC)
+	@$(ECHO)  FPC Version.. $(FPC_VERSION)
+	@$(ECHO)  Source CPU... $(CPU_SOURCE)
+	@$(ECHO)  Target CPU... $(CPU_TARGET)
+	@$(ECHO)  Source OS.... $(OS_SOURCE)
+	@$(ECHO)  Target OS.... $(OS_TARGET)
+	@$(ECHO)  Full Source.. $(FULL_SOURCE)
+	@$(ECHO)  Full Target.. $(FULL_TARGET)
+	@$(ECHO)  SourceSuffix. $(SOURCESUFFIX)
+	@$(ECHO)  TargetSuffix. $(TARGETSUFFIX)
+	@$(ECHO)  FPC fpmake... $(FPCFPMAKE)
+	@$(ECHO)
+	@$(ECHO)  == Directory info ==
+	@$(ECHO)
+	@$(ECHO)  Required pkgs... $(REQUIRE_PACKAGES)
+	@$(ECHO)
+	@$(ECHO)  Basedir......... $(BASEDIR)
+	@$(ECHO)  FPCDir.......... $(FPCDIR)
+	@$(ECHO)  CrossBinDir..... $(CROSSBINDIR)
+	@$(ECHO)  UnitsDir........ $(UNITSDIR)
+	@$(ECHO)  PackagesDir..... $(PACKAGESDIR)
+	@$(ECHO)
+	@$(ECHO)  GCC library..... $(GCCLIBDIR)
+	@$(ECHO)  Other library... $(OTHERLIBDIR)
+	@$(ECHO)
+	@$(ECHO)  == Tools info ==
+	@$(ECHO)
+	@$(ECHO)  As........ $(AS)
+	@$(ECHO)  Ld........ $(LD)
+	@$(ECHO)  Ar........ $(AR)
+	@$(ECHO)  Rc........ $(RC)
+	@$(ECHO)
+	@$(ECHO)  Mv........ $(MVPROG)
+	@$(ECHO)  Cp........ $(CPPROG)
+	@$(ECHO)  Rm........ $(RMPROG)
+	@$(ECHO)  GInstall.. $(GINSTALL)
+	@$(ECHO)  Echo...... $(ECHO)
+	@$(ECHO)  Shell..... $(SHELL)
+	@$(ECHO)  Date...... $(DATE)
+	@$(ECHO)  FPCMake... $(FPCMAKE)
+	@$(ECHO)  PPUMove... $(PPUMOVE)
+	@$(ECHO)  Zip....... $(ZIPPROG)
+	@$(ECHO)
+	@$(ECHO)  == Object info ==
+	@$(ECHO)
+	@$(ECHO)  Target Loaders........ $(TARGET_LOADERS)
+	@$(ECHO)  Target Units.......... $(TARGET_UNITS)
+	@$(ECHO)  Target Implicit Units. $(TARGET_IMPLICITUNITS)
+	@$(ECHO)  Target Programs....... $(TARGET_PROGRAMS)
+	@$(ECHO)  Target Dirs........... $(TARGET_DIRS)
+	@$(ECHO)  Target Examples....... $(TARGET_EXAMPLES)
+	@$(ECHO)  Target ExampleDirs.... $(TARGET_EXAMPLEDIRS)
+	@$(ECHO)
+	@$(ECHO)  Clean Units......... $(CLEAN_UNITS)
+	@$(ECHO)  Clean Files......... $(CLEAN_FILES)
+	@$(ECHO)
+	@$(ECHO)  Install Units....... $(INSTALL_UNITS)
+	@$(ECHO)  Install Files....... $(INSTALL_FILES)
+	@$(ECHO)
+	@$(ECHO)  == Install info ==
+	@$(ECHO)
+	@$(ECHO)  DateStr.............. $(DATESTR)
+	@$(ECHO)  ZipName.............. $(ZIPNAME)
+	@$(ECHO)  ZipPrefix............ $(ZIPPREFIX)
+	@$(ECHO)  ZipCrossPrefix....... $(ZIPCROSSPREFIX)
+	@$(ECHO)  ZipSuffix............ $(ZIPSUFFIX)
+	@$(ECHO)  FullZipName.......... $(FULLZIPNAME)
+	@$(ECHO)  Install FPC Package.. $(INSTALL_FPCPACKAGE)
+	@$(ECHO)
+	@$(ECHO)  Install base dir..... $(INSTALL_BASEDIR)
+	@$(ECHO)  Install binary dir... $(INSTALL_BINDIR)
+	@$(ECHO)  Install library dir.. $(INSTALL_LIBDIR)
+	@$(ECHO)  Install units dir.... $(INSTALL_UNITDIR)
+	@$(ECHO)  Install source dir... $(INSTALL_SOURCEDIR)
+	@$(ECHO)  Install doc dir...... $(INSTALL_DOCDIR)
+	@$(ECHO)  Install example dir.. $(INSTALL_EXAMPLEDIR)
+	@$(ECHO)  Install data dir..... $(INSTALL_DATADIR)
+	@$(ECHO)
+	@$(ECHO)  Dist destination dir. $(DIST_DESTDIR)
+	@$(ECHO)  Dist zip name........ $(DIST_ZIPNAME)
+	@$(ECHO)
+.PHONY: fpc_info
+fpc_info: $(INFORULES)
+.PHONY: fpc_makefile fpc_makefiles fpc_makefile_sub1 fpc_makefile_sub2 \
+	fpc_makefile_dirs
+fpc_makefile:
+	$(FPCMAKE) -w -T$(OS_TARGET) Makefile.fpc
+fpc_makefile_sub1:
+ifdef TARGET_DIRS
+	$(FPCMAKE) -w -T$(OS_TARGET) $(addsuffix /Makefile.fpc,$(TARGET_DIRS))
+endif
+ifdef TARGET_EXAMPLEDIRS
+	$(FPCMAKE) -w -T$(OS_TARGET) $(addsuffix /Makefile.fpc,$(TARGET_EXAMPLEDIRS))
+endif
+fpc_makefile_sub2: $(addsuffix _makefile_dirs,$(TARGET_DIRS) $(TARGET_EXAMPLEDIRS))
+fpc_makefile_dirs: fpc_makefile_sub1 fpc_makefile_sub2
+fpc_makefiles: fpc_makefile fpc_makefile_dirs
+units:
+examples:
+shared:
+sourceinstall: fpc_sourceinstall
+exampleinstall: fpc_exampleinstall
+zipexampleinstall: fpc_zipexampleinstall
+info: fpc_info
+makefiles: fpc_makefiles
+.PHONY: units examples shared sourceinstall exampleinstall zipexampleinstall info makefiles
+ifneq ($(wildcard fpcmake.loc),)
+include fpcmake.loc
+endif
+override FPCOPT:=$(filter-out -FU%,$(FPCOPT))
+override FPCOPT:=$(filter-out -FE%,$(FPCOPT))
+override FPCOPT:=$(filter-out $(addprefix -Fu,$(COMPILER_UNITDIR)),$(FPCOPT))# Compose general fpmake-parameters
+ifdef FPMAKEOPT
+FPMAKE_OPT+=$(FPMAKEOPT)
+endif
+FPMAKE_OPT+=--localunitdir=../..
+FPMAKE_OPT+=--globalunitdir=..
+FPMAKE_OPT+=$(FPC_TARGETOPT)
+FPMAKE_OPT+=$(addprefix -o ,$(FPCOPT))
+FPMAKE_OPT+=--compiler=$(FPC)
+FPMAKE_OPT+=-bu
+.NOTPARALLEL:
+fpmake$(SRCEXEEXT): fpmake.pp
+	$(FPCFPMAKE) fpmake.pp $(FPMAKE_SKIP_CONFIG) $(addprefix -Fu,$(COMPILER_FPMAKE_UNITDIR)) $(FPCMAKEOPT) $(OPT)
+all:	fpmake$(SRCEXEEXT)
+	$(LOCALFPMAKE) compile $(FPMAKE_OPT)
+smart:	fpmake$(SRCEXEEXT)
+	$(LOCALFPMAKE) compile $(FPMAKE_OPT) -o -XX -o -CX
+release:	fpmake$(SRCEXEEXT)
+	$(LOCALFPMAKE) compile $(FPMAKE_OPT) -o -dRELEASE
+debug:	fpmake$(SRCEXEEXT)
+	$(LOCALFPMAKE) compile $(FPMAKE_OPT) -o -dDEBUG
+ifeq ($(FPMAKE_BIN_CLEAN),)
+clean:
+else
+clean:
+	$(FPMAKE_BIN_CLEAN) clean $(FPMAKE_OPT)
+endif
+ifeq ($(FPMAKE_BIN_CLEAN),)
+distclean:	$(addsuffix _distclean,$(TARGET_DIRS)) fpc_cleanall
+else
+distclean:
+ifdef inUnix
+	{ $(FPMAKE_BIN_CLEAN) distclean $(FPMAKE_OPT); if [ $$? != "0" ]; then { echo Something wrong with fpmake exectable. Remove the executable and call make recursively to recover.; $(DEL) $(FPMAKE_BIN_CLEAN); $(MAKE) fpc_cleanall; }; fi;  }
+else
+	$(FPMAKE_BIN_CLEAN) distclean $(FPMAKE_OPT)
+endif
+	-$(DEL) $(LOCALFPMAKE)
+endif
+cleanall: distclean
+install:	fpmake$(SRCEXEEXT)
+ifdef UNIXHier
+	$(LOCALFPMAKE) install $(FPMAKE_OPT) --prefix=$(INSTALL_PREFIX) --baseinstalldir=$(INSTALL_LIBDIR)/fpc/$(FPC_VERSION) --unitinstalldir=$(INSTALL_UNITDIR)
+else
+	$(LOCALFPMAKE) install $(FPMAKE_OPT) --prefix=$(INSTALL_BASEDIR) --baseinstalldir=$(INSTALL_BASEDIR) --unitinstalldir=$(INSTALL_UNITDIR)
+endif
+distinstall:	fpmake$(SRCEXEEXT)
+ifdef UNIXHier
+	$(LOCALFPMAKE) install $(FPMAKE_OPT) --prefix=$(INSTALL_PREFIX) --baseinstalldir=$(INSTALL_LIBDIR)/fpc/$(FPC_VERSION) --unitinstalldir=$(INSTALL_UNITDIR) -ie -fsp 0
+else
+	$(LOCALFPMAKE) install $(FPMAKE_OPT) --prefix=$(INSTALL_BASEDIR) --baseinstalldir=$(INSTALL_BASEDIR) --unitinstalldir=$(INSTALL_UNITDIR) -ie -fsp 0
+endif
+zipinstall:	fpmake$(SRCEXEEXT)
+	$(LOCALFPMAKE) zipinstall $(FPMAKE_OPT) --zipprefix=$(DIST_DESTDIR)/$(ZIPPREFIX)
+zipdistinstall:	fpmake$(SRCEXEEXT)
+	$(LOCALFPMAKE) zipinstall $(FPMAKE_OPT) --zipprefix=$(DIST_DESTDIR)/$(ZIPPREFIX) -ie -fsp 0
+zipsourceinstall:	fpmake$(SRCEXEEXT)
+ifdef UNIXHier
+	$(LOCALFPMAKE) archive $(FPMAKE_OPT) --zipprefix=$(DIST_DESTDIR)/$(ZIPPREFIX) --prefix=share/src/fpc-\$$\(PACKAGEVERSION\)/$(INSTALL_FPCSUBDIR)/\$$\(PACKAGEDIRECTORY\)
+else
+	$(LOCALFPMAKE) archive $(FPMAKE_OPT) --zipprefix=$(DIST_DESTDIR)/$(ZIPPREFIX) --prefix=source\\$(INSTALL_FPCSUBDIR)\\\$$\(PACKAGEDIRECTORY\)
+endif

+ 102 - 0
packages/fcl-css/Makefile.fpc

@@ -0,0 +1,102 @@
+#
+#   Makefile.fpc for running fpmake
+#
+
+[package]
+name=fcl-css
+version=3.3.1
+
+[require]
+packages=rtl fcl-base fpmkunit
+
+[install]
+fpcpackage=y
+
+[default]
+fpcdir=../..
+
+[prerules]
+FPMAKE_BIN_CLEAN=$(wildcard ./fpmake$(SRCEXEEXT))
+ifdef OS_TARGET
+FPC_TARGETOPT+=--os=$(OS_TARGET)
+endif
+ifdef CPU_TARGET
+FPC_TARGETOPT+=--cpu=$(CPU_TARGET)
+endif
+LOCALFPMAKE=./fpmake$(SRCEXEEXT)
+
+[rules]
+# Do not pass the Makefile's unit and binary target locations. Fpmake uses it's own.
+override FPCOPT:=$(filter-out -FU%,$(FPCOPT))
+override FPCOPT:=$(filter-out -FE%,$(FPCOPT))
+# Do not pass the package-unitdirectories. Fpmake adds those and this way they don't apear in the .fpm
+override FPCOPT:=$(filter-out $(addprefix -Fu,$(COMPILER_UNITDIR)),$(FPCOPT))# Compose general fpmake-parameters
+# Compose general fpmake-parameters
+ifdef FPMAKEOPT
+FPMAKE_OPT+=$(FPMAKEOPT)
+endif
+FPMAKE_OPT+=--localunitdir=../..
+FPMAKE_OPT+=--globalunitdir=..
+FPMAKE_OPT+=$(FPC_TARGETOPT)
+FPMAKE_OPT+=$(addprefix -o ,$(FPCOPT))
+FPMAKE_OPT+=--compiler=$(FPC)
+FPMAKE_OPT+=-bu
+.NOTPARALLEL:
+
+fpmake$(SRCEXEEXT): fpmake.pp
+	$(FPCFPMAKE) fpmake.pp $(FPMAKE_SKIP_CONFIG) $(addprefix -Fu,$(COMPILER_FPMAKE_UNITDIR)) $(FPCMAKEOPT) $(OPT)
+all:	fpmake$(SRCEXEEXT)
+	$(LOCALFPMAKE) compile $(FPMAKE_OPT)
+smart:	fpmake$(SRCEXEEXT)
+	$(LOCALFPMAKE) compile $(FPMAKE_OPT) -o -XX -o -CX
+release:	fpmake$(SRCEXEEXT)
+	$(LOCALFPMAKE) compile $(FPMAKE_OPT) -o -dRELEASE
+debug:	fpmake$(SRCEXEEXT)
+	$(LOCALFPMAKE) compile $(FPMAKE_OPT) -o -dDEBUG
+# If no fpmake exists and (dist)clean is called, do not try to build fpmake, it will
+# most often fail because the dependencies are cleared.
+# In case of a clean, simply do nothing
+ifeq ($(FPMAKE_BIN_CLEAN),)
+clean:
+else
+clean:
+	$(FPMAKE_BIN_CLEAN) clean $(FPMAKE_OPT)
+endif
+# In case of a distclean, perform an 'old'-style distclean. This to avoid problems
+# when the package is compiled using fpcmake prior to running this clean using fpmake
+ifeq ($(FPMAKE_BIN_CLEAN),)
+distclean:	$(addsuffix _distclean,$(TARGET_DIRS)) fpc_cleanall
+else
+distclean:
+ifdef inUnix
+        { $(FPMAKE_BIN_CLEAN) distclean $(FPMAKE_OPT); if [ $$? != "0" ]; then { echo Something wrong with fpmake exectable. Remove the executable and call make recursively to recover.; $(DEL) $(FPMAKE_BIN_CLEAN); $(MAKE) fpc_cleanall; }; fi;  }
+else
+        $(FPMAKE_BIN_CLEAN) distclean $(FPMAKE_OPT)
+endif
+	-$(DEL) $(LOCALFPMAKE)
+endif
+cleanall: distclean
+install:	fpmake$(SRCEXEEXT)
+ifdef UNIXHier
+	$(LOCALFPMAKE) install $(FPMAKE_OPT) --prefix=$(INSTALL_PREFIX) --baseinstalldir=$(INSTALL_LIBDIR)/fpc/$(FPC_VERSION) --unitinstalldir=$(INSTALL_UNITDIR)
+else
+	$(LOCALFPMAKE) install $(FPMAKE_OPT) --prefix=$(INSTALL_BASEDIR) --baseinstalldir=$(INSTALL_BASEDIR) --unitinstalldir=$(INSTALL_UNITDIR)
+endif
+# distinstall also installs the example-sources and omits the location of the source-
+# files from the fpunits.cfg files.
+distinstall:	fpmake$(SRCEXEEXT)
+ifdef UNIXHier
+	$(LOCALFPMAKE) install $(FPMAKE_OPT) --prefix=$(INSTALL_PREFIX) --baseinstalldir=$(INSTALL_LIBDIR)/fpc/$(FPC_VERSION) --unitinstalldir=$(INSTALL_UNITDIR) -ie -fsp 0
+else
+	$(LOCALFPMAKE) install $(FPMAKE_OPT) --prefix=$(INSTALL_BASEDIR) --baseinstalldir=$(INSTALL_BASEDIR) --unitinstalldir=$(INSTALL_UNITDIR) -ie -fsp 0
+endif
+zipinstall:	fpmake$(SRCEXEEXT)
+	$(LOCALFPMAKE) zipinstall $(FPMAKE_OPT) --zipprefix=$(DIST_DESTDIR)/$(ZIPPREFIX)
+zipdistinstall:	fpmake$(SRCEXEEXT)
+	$(LOCALFPMAKE) zipinstall $(FPMAKE_OPT) --zipprefix=$(DIST_DESTDIR)/$(ZIPPREFIX) -ie -fsp 0
+zipsourceinstall:	fpmake$(SRCEXEEXT)
+ifdef UNIXHier
+	$(LOCALFPMAKE) archive $(FPMAKE_OPT) --zipprefix=$(DIST_DESTDIR)/$(ZIPPREFIX) --prefix=share/src/fpc-\$$\(PACKAGEVERSION\)/$(INSTALL_FPCSUBDIR)/\$$\(PACKAGEDIRECTORY\)
+else
+	$(LOCALFPMAKE) archive $(FPMAKE_OPT) --zipprefix=$(DIST_DESTDIR)/$(ZIPPREFIX) --prefix=source\\$(INSTALL_FPCSUBDIR)\\\$$\(PACKAGEDIRECTORY\)
+endif

+ 118 - 0
packages/fcl-css/examples/cssmin.lpr

@@ -0,0 +1,118 @@
+{ Demo for CSS engine : minimize a CSS file
+
+  Copyright (C) 2022- michael Van Canneyt [email protected]
+
+  This source 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 code 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.
+
+  A copy of the GNU General Public License is available on the World Wide Web at <http://www.gnu.org/copyleft/gpl.html>. You can
+  also obtain it by writing to the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1335, USA.
+}
+program cssmin;
+
+{$mode objfpc}{$H+}
+
+uses
+  Classes, SysUtils, CustApp, fpcssutils;
+
+type
+
+  { TCSSMinimizerApplication }
+
+  TCSSMinimizerApplication = class(TCustomApplication)
+  protected
+    FInputFile,
+    FOutputFile : String;
+
+    procedure DoRun; override;
+  public
+    Function ParseOptions : String;
+    procedure Minimize(const aInputFile,aOutputFile : String);
+    constructor Create(TheOwner: TComponent); override;
+    destructor Destroy; override;
+    procedure Usage(Msg : string); virtual;
+  end;
+
+{ TCSSMinimizerApplication }
+
+procedure TCSSMinimizerApplication.DoRun;
+
+var
+  ErrorMsg: String;
+
+begin
+  Terminate;
+  ErrorMsg:=CheckOptions('hi:o:', ['help','input:','output:']);
+  if (ErrorMsg='') and not HasOption('h','help') then
+    ErrorMsg:=ParseOptions;
+  if HasOption('h','help') or (ErrorMsg<>'') then
+    begin
+    Usage(ErrorMsg);
+    Exit;
+    end;
+  Minimize(FInputFile,FOutputFile);
+end;
+
+function TCSSMinimizerApplication.ParseOptions: String;
+begin
+  Result:='';
+  FInputFile:=GetOptionValue('i','input');
+  FOutputFile:=GetOptionValue('o','output');
+  if FInputFile='' then
+    Exit('Need input file');
+  if FOutputFile='' then
+    FOutputFile:=ChangeFileExt(FInputFile,'.min.css');
+end;
+
+procedure TCSSMinimizerApplication.Minimize(const aInputFile, aOutputFile: String);
+
+Var
+  SIn,SOut : TMemoryStream;
+  Util : TCSSUtils;
+
+begin
+  SOut:=Nil;
+  Util:=Nil;
+  SIn:=TMemoryStream.Create;
+  try
+    Sin.LoadFromFile(aInputFile);
+    Sout:=TmemoryStream.Create;
+    Util:=TCSSUtils.Create(Self);
+    Util.Minimize(Sin,Sout);
+    Sout.SaveToFile(aOutputFile);
+  finally
+    Sin.Free;
+    SOut.Free;
+    Util.Free;
+  end;
+end;
+
+constructor TCSSMinimizerApplication.Create(TheOwner: TComponent);
+begin
+  inherited Create(TheOwner);
+  StopOnException:=True;
+end;
+
+destructor TCSSMinimizerApplication.Destroy;
+begin
+  inherited Destroy;
+end;
+
+procedure TCSSMinimizerApplication.Usage(Msg: string);
+begin
+  writeln('Usage: ', ExeName, ' -h');
+  exitcode:=Ord(Msg<>'')
+end;
+
+var
+  Application: TCSSMinimizerApplication;
+begin
+  Application:=TCSSMinimizerApplication.Create(nil);
+  Application.Title:='CSS Minimizer Application';
+  Application.Run;
+  Application.Free;
+end.
+

+ 126 - 0
packages/fcl-css/examples/extractcssclasses.lpr

@@ -0,0 +1,126 @@
+{ Demo for CSS engine : extract class names from a list of CSS files
+
+  Copyright (C) 2022- michael Van Canneyt [email protected]
+
+  This source 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 code 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.
+
+  A copy of the GNU General Public License is available on the World Wide Web at <http://www.gnu.org/copyleft/gpl.html>. You can
+  also obtain it by writing to the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1335, USA.
+}
+
+program extractcssclasses;
+
+{$mode objfpc}{$H+}
+
+uses
+  Classes, SysUtils, CustApp, fpcssutils, fpcsstree;
+
+type
+
+  { TCSSClassNamesApplication }
+
+  TCSSClassNamesApplication = class(TCustomApplication)
+  private
+    procedure OutputClassNames(const aFileName, aOutput: String);
+  protected
+    procedure DoRun; override;
+  public
+    constructor Create(TheOwner: TComponent); override;
+    destructor Destroy; override;
+    procedure Usage(aError : String); virtual;
+  end;
+
+{ TCSSClassNamesApplication }
+
+procedure TCSSClassNamesApplication.OutputClassNames(const aFileName,aOutput : String);
+
+Var
+  L : TStringList;
+  Util : TCSSUtils;
+
+begin
+  Util:=Nil;
+  L:=TStringList.Create;
+  try
+    Util:=TCSSUtils.Create(Self);
+    L.Sorted:=True;
+    L.Duplicates:=dupIgnore;
+    L.Options:=L.Options-[soTrailingLineBreak];
+    if (aOutput<>'') and FileExists(aOutput) then
+      L.LoadFromFile(aOutput);
+    Util.ExtractClassNames(aFileName,L);
+    if aOutput='' then
+      begin
+      if L.Count>0 then
+        Writeln(L.Text);
+      end
+    else
+      L.SaveToFile(aOutput);
+  finally
+    Util.Free;
+    L.Free;
+  end;
+end;
+
+procedure TCSSClassNamesApplication.DoRun;
+
+Const
+  Opts = 'ho::';
+  LongOpts : Array of string = ('help','output::');
+
+var
+  FN, FNOutput : String;
+  ErrorMsg: String;
+  files : TStringArray;
+
+begin
+  Terminate;
+  ErrorMsg:=CheckOptions(Opts,LongOpts);
+  if HasOption('h','help') or (ErrorMsg<>'') then
+    begin
+    Usage(ErrorMsg);
+    Exit;
+    end;
+  Files:=GetNonOptions(Opts,LongOpts);
+  FNOutput:=GetOptionValue('o','output');
+  if (FNOutput<>'') and FileExists(FNOutput) then
+    DeleteFile(FNOutput);
+  For FN in Files do
+    OutputClassNames(FN,FNOUtput);
+end;
+
+constructor TCSSClassNamesApplication.Create(TheOwner: TComponent);
+begin
+  inherited Create(TheOwner);
+  StopOnException:=True;
+end;
+
+destructor TCSSClassNamesApplication.Destroy;
+begin
+  inherited Destroy;
+end;
+
+procedure TCSSClassNamesApplication.Usage(aError: String);
+begin
+  if aError<>'' then
+    Writeln('Error: ',aError);
+  Writeln('Usage: ', ExeName, ' [options] FILES');
+  Writeln('where options is one or more of:');
+  Writeln('-h --help           This help message');
+  Writeln('-o --output=FILE    Write class names to this file.');
+  exitCode:=Ord(aError<>'');
+end;
+
+var
+  Application: TCSSClassNamesApplication;
+begin
+  Application:=TCSSClassNamesApplication.Create(nil);
+  Application.Title:='Extract CSS Application';
+  Application.Run;
+  Application.Free;
+end.
+

+ 64 - 0
packages/fcl-css/fpmake.pp

@@ -0,0 +1,64 @@
+{$ifndef ALLPACKAGES}
+{$mode objfpc}{$H+}
+program fpmake;
+
+uses fpmkunit;
+
+Var
+  T : TTarget;
+  P : TPackage;
+  D : TDependency;
+begin
+  With Installer do
+    begin
+{$endif ALLPACKAGES}
+
+    P:=AddPackage('fcl-css');
+    P.ShortName:='fclcss';
+{$ifdef ALLPACKAGES}
+    P.Directory:=ADirectory;
+{$endif ALLPACKAGES}
+    P.Version:='3.3.1';
+    P.Options.Add('-S2h');
+    D:=P.Dependencies.Add('fcl-base');
+
+    P.Author := 'Michael Van Canneyt';
+    P.License := 'LGPL with modification, ';
+    P.HomepageURL := 'www.freepascal.org';
+    P.Email := '';
+    P.Description := 'CSS parsing and utility functions.';
+    P.NeedLibC:= false;
+    P.OSes:=AllOSes-[embedded,msdos,win16,macosclassic,palmos,zxspectrum,msxdos,amstradcpc,sinclairql];
+    if Defaults.CPU=jvm then
+      P.OSes := P.OSes - [java,android];
+
+    P.SourcePath.Add('src');
+    P.IncludePath.Add('src');
+
+    T:=P.Targets.AddUnit('fpcsstree.pp');
+    T:=P.Targets.AddUnit('fpcssscanner.pp');
+    T.ResourceStrings:=True;
+    T:=P.Targets.AddUnit('fpcssparser.pp');
+    T.ResourceStrings:=True;
+      with T.Dependencies do
+        begin
+          AddUnit('fpcsstree');
+          AddUnit('fpcssscanner');
+        end;
+    T:=P.Targets.AddUnit('fpcssutils.pp');
+      with T.Dependencies do
+        begin
+          AddUnit('fpcsstree');
+          AddUnit('fpcssscanner');
+          AddUnit('fpcssparser');
+        end;
+    P.ExamplePath.Add('examples');
+    P.Targets.AddExampleProgram('examples/cssmin.lpr');
+    P.Targets.AddExampleProgram('examples/extractcssclasses.lpr');
+
+{$ifndef ALLPACKAGES}
+    Run;
+    end;
+end.
+{$endif ALLPACKAGES}
+

+ 897 - 0
packages/fcl-css/src/fpcssparser.pp

@@ -0,0 +1,897 @@
+{
+    This file is part of the Free Pascal Run time library.
+    Copyright (c) 2022- by Michael Van Canneyt ([email protected])
+
+    This file contains a CSS parser
+
+    See the File COPYING.FPC, included in this distribution,
+    for details about the copyright.
+
+    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.
+
+ **********************************************************************}
+unit fpCSSParser;
+
+{$mode ObjFPC}{$H+}
+
+{ $DEFINE debugparser}
+
+interface
+
+uses
+  TypInfo, Classes, SysUtils, fpcsstree, fpcssscanner;
+
+Type
+  ECSSParser = Class(Exception);
+  { TCSSParser }
+
+  TCSSParser = class(TObject)
+  private
+    FInput : TStream;
+    FScanner: TCSSScanner;
+    FPrevious : TCSSToken;
+    FCurrent : TCSSToken;
+    FCurrentTokenString : UTF8String;
+    FPeekToken : TCSSToken;
+    FPeekTokenString : UTF8String;
+    FFreeScanner : Boolean;
+    function CreateElement(aClass: TCSSElementClass): TCSSElement;
+    class function GetAppendElement(aList: TCSSListElement): TCSSElement;
+    function GetAtEOF: Boolean;
+    function GetCurSource: UTF8String;
+    Function GetCurLine : Integer;
+    Function GetCurPos : Integer;
+  protected
+    Procedure DoError(Msg : UTF8String);
+    Procedure DoError(Fmt : UTF8String; Args : Array of const);
+    Procedure Consume(aToken : TCSSToken);
+    function ParseComponentValueList(allowRules: Boolean=True): TCSSElement;
+    function ParseComponentValue: TCSSElement;
+    function ParseExpression: TCSSElement;
+    function ParseRule(IsAt: Boolean=False): TCSSElement;
+    function ParseAtRule: TCSSElement;
+    function ParseRuleList(aStopOn : TCSStoken = ctkEOF): TCSSElement;
+    function ParseSelector: TCSSElement;
+    function ParseDeclaration(aIsAt : Boolean = false): TCSSDeclarationElement;
+    function ParseCall(aName: UTF8String): TCSSElement;
+    function ParseUnary: TCSSElement;
+    function ParseUnit: TCSSUnits;
+    function ParseIdentifier : TCSSElement;
+    function ParseParenthesis: TCSSElement;
+    function ParsePseudo: TCSSElement;
+    Function ParseRuleBody(aRule: TCSSRuleElement; aIsAt : Boolean = False) : integer;
+    function ParseInteger: TCSSElement;
+    function ParseFloat: TCSSElement;
+    function ParseString: TCSSElement;
+    Function ParseUnicodeRange : TCSSElement;
+    function ParseArray(aPrefix: TCSSElement): TCSSElement;
+    function ParseURL: TCSSElement;
+    Property CurrentSource : UTF8String Read GetCurSource;
+    Property CurrentLine : Integer Read GetCurLine;
+    Property CurrentPos : Integer Read GetCurPos;
+  Public
+    Constructor Create(AInput: TStream);
+    Constructor Create(AScanner : TCSSScanner); virtual;
+    Destructor Destroy; override;
+    Function Parse : TCSSElement;
+    Property CurrentToken : TCSSToken Read FCurrent;
+    Property CurrentTokenString : UTF8String Read FCurrentTokenString;
+    Function GetNextToken : TCSSToken;
+    Function PeekNextToken : TCSSToken;
+    Property Scanner : TCSSScanner Read FScanner;
+    Property atEOF : Boolean Read GetAtEOF;
+  end;
+
+Function TokenToBinaryOperation(aToken : TCSSToken)  : TCSSBinaryOperation;
+Function TokenToUnaryOperation(aToken : TCSSToken) : TCSSUnaryOperation;
+
+implementation
+
+Resourcestring
+  SBinaryInvalidToken = 'Invalid token for binary operation: %s';
+  SUnaryInvalidToken = 'Invalid token for unary operation: %s';
+  SErrFileSource = 'Error: file "%s" line %d, pos %d: ';
+  SErrSource = 'Error: line %d, pos %d: ';
+  SErrUnexpectedToken = 'Unexpected token: Got %s (as UTF8String: "%s"), expected: %s ';
+  SErrInvalidFloat = 'Invalid float: %s';
+  SErrUnexpectedEndOfFile = 'Unexpected EOF while scanning function args: %s';
+
+Function TokenToBinaryOperation(aToken : TCSSToken)  : TCSSBinaryOperation;
+
+begin
+  Case aToken of
+    ctkEquals : Result:=boEquals;
+    ctkPlus : Result:=boPlus;
+    ctkMinus:  Result:=boMinus;
+    ctkAnd : result:=boAnd;
+    ctkLT : Result:=boLT;
+    ctkDIV : Result:=boDIV;
+    ctkStar : Result:=boSTAR;
+    ctkTilde : Result:=boTilde;
+    ctkColon : Result:=boCOLON;
+    ctkDoubleColon : Result:=boDoubleColon;
+    ctkSquared : Result:=boSquared;
+    ctkGT : Result:=boGT;
+  else
+    Raise ECSSParser.CreateFmt(SBinaryInvalidToken,[GetEnumName(TypeInfo(aToken),Ord(aToken))]);
+    // Result:=boEquals;
+  end;
+end;
+
+Function TokenToUnaryOperation(aToken : TCSSToken) : TCSSUnaryOperation;
+
+begin
+  Case aToken of
+    ctkDOUBLECOLON: Result:=uoDoubleColon;
+    ctkMinus: Result:=uoMinus;
+    ctkPlus: Result:=uoPlus;
+    ctkDiv: Result:=uoDiv;
+  else
+    Raise ECSSParser.CreateFmt(SUnaryInvalidToken,[GetEnumName(TypeInfo(aToken),Ord(aToken))]);
+  end;
+end;
+
+{ TCSSParser }
+
+function TCSSParser.GetAtEOF: Boolean;
+begin
+  Result:=(CurrentToken=ctkEOF);
+end;
+
+procedure TCSSParser.DoError(Msg: UTF8String);
+Var
+  ErrAt : UTF8String;
+
+begin
+  If Assigned(FScanner) then
+    If FScanner.CurFilename<>'' then
+      ErrAt:=Format(SErrFileSource,[FScanner.CurFileName,FScanner.CurRow,FScanner.CurColumn])
+    else
+      ErrAt:=Format(SErrSource,[FScanner.Currow,FScanner.CurColumn]);
+  Raise ECSSParser.Create(ErrAt+Msg)
+end;
+
+procedure TCSSParser.DoError(Fmt: UTF8String; Args: array of const);
+begin
+  DoError(Format(Fmt,Args));
+end;
+
+procedure TCSSParser.Consume(aToken: TCSSToken);
+begin
+  if CurrentToken<>aToken then
+    DoError(SErrUnexpectedToken ,[
+             GetEnumName(TypeInfo(TCSSToken),Ord(CurrentToken)),
+             CurrentTokenString,
+             GetEnumName(TypeInfo(TCSSToken),Ord(aToken))
+             ]);
+  GetNextToken;
+end;
+
+
+function TCSSParser.GetCurSource: UTF8String;
+begin
+  If Assigned(FScanner) then
+    Result:=FScanner.CurFileName
+  else
+    Result:='';
+end;
+
+function TCSSParser.GetCurLine: Integer;
+begin
+  if Assigned(FScanner) then
+    Result:=FScanner.CurRow
+  else
+    Result:=0;
+end;
+
+function TCSSParser.GetCurPos: Integer;
+begin
+  if Assigned(FScanner) then
+    Result:=FScanner.CurColumn
+  else
+    Result:=0;
+end;
+
+constructor TCSSParser.Create(AInput: TStream);
+begin
+  FInput:=AInput;
+  Create(TCSSScanner.Create(FInput));
+  FFreeScanner:=True;
+end;
+
+constructor TCSSParser.Create(AScanner: TCSSScanner);
+begin
+  FCurrent:=ctkUNKNOWN;
+  FPeekToken:=ctkUNKNOWN;
+  FPeekTokenString:='';
+  FScanner:=aScanner;
+end;
+
+destructor TCSSParser.Destroy;
+begin
+  if FFreeScanner then
+    FreeAndNil(FScanner);
+  inherited Destroy;
+end;
+
+Class Function TCSSParser.GetAppendElement(aList : TCSSListElement) : TCSSElement;
+
+begin
+  Case aList.ChildCount of
+    0 : Result:=Nil;
+    1 : Result:=aList.ExtractElement(0);
+  else
+    Result:=aList;
+  end;
+  if Result<>aList then
+    aList.Free;
+end;
+
+function TCSSParser.ParseAtRule: TCSSElement;
+
+Var
+  aRule : TCSSRuleElement;
+  aSel : TCSSElement;
+  Term : TCSSTokens;
+  aLast : TCSSToken;
+  aList : TCSSListElement;
+
+begin
+//  Writeln('Parse rule. IsAt:',IsAt);
+  Term:=[ctkLBRACE,ctkEOF,ctkSEMICOLON];
+  aRule:=TCSSAtRuleElement(CreateElement(TCSSAtRuleElement));
+  TCSSAtRuleElement(aRule).AtKeyWord:=CurrentTokenString;
+  Consume(ctkAtKeyWord);
+  try
+    aList:=TCSSListElement(CreateElement(TCSSListElement));
+    While Not (CurrentToken in Term) do
+      begin
+      aSel:=ParseSelector;
+      aList.AddChild(aSel);
+      if CurrentToken=ctkCOMMA then
+        begin
+        Consume(ctkCOMMA);
+        aRule.AddSelector(GetAppendElement(aList));
+        aList:=TCSSListElement(CreateElement(TCSSListElement));
+        end;
+      end;
+    aRule.AddSelector(GetAppendElement(aList));
+    aLast:=CurrentToken;
+    if (aLast<>ctkSEMICOLON) then
+      begin
+      Consume(ctkLBRACE);
+      aRule.AddChild(ParseRuleList(ctkRBRACE));
+      Consume(ctkRBRACE);
+      end;
+    Result:=aRule;
+  except
+    aRule.Free;
+    Raise;
+  end;
+
+end;
+
+function TCSSParser.ParseExpression: TCSSElement;
+
+    Function AllowRules(aName : String) : Boolean;
+
+    begin
+      result:=sameText(aName,'@media') or sameText(aName,'@print');
+    end;
+
+Const
+  RuleTokens =
+       [ctkIDENTIFIER,ctkCLASSNAME,ctkHASH,ctkINTEGER,
+        ctkDOUBLECOLON,ctkSTAR,ctkTILDE,ctkCOLON,ctkLBRACKET];
+
+begin
+  if CurrentToken in RuleTokens then
+    Result:=ParseRule
+  else if CurrentToken=ctkATKEYWORD then
+    begin
+    if AllowRules(CurrentTokenString) then
+      Result:=ParseAtRule
+    else
+      Result:=ParseRule(True);
+    end
+  else
+    Result:=ParseComponentValueList;
+end;
+
+function TCSSParser.ParseRuleList(aStopOn : TCSStoken = ctkEOF): TCSSElement;
+
+Var
+  aList : TCSSCompoundElement;
+  aEl : TCSSElement;
+  Terms : TCSSTokens;
+begin
+  Terms:=[ctkEOF];
+  Include(Terms,aStopOn);
+  aList:=TCSSCompoundElement(CreateElement(TCSSCompoundElement));
+  Try
+    While not (CurrentToken in Terms) do
+      begin
+      aEl:=ParseExpression;
+      aList.AddChild(aEl);
+      if CurrentToken=ctkSEMICOLON then
+        Consume(ctkSEMICOLON);
+      end;
+    Result:=aList;
+  except
+    aList.Free;
+    Raise;
+  end;
+end;
+
+function TCSSParser.Parse: TCSSElement;
+begin
+  GetNextToken;
+  if CurrentToken=ctkLBRACE then
+    Result:=ParseRule
+  else
+    Result:=ParseRuleList;
+end;
+
+
+function TCSSParser.GetNextToken: TCSSToken;
+begin
+  FPrevious:=FCurrent;
+  If (FPeekToken<>ctkUNKNOWN) then
+     begin
+     FCurrent:=FPeekToken;
+     FCurrentTokenString:=FPeekTokenString;
+     FPeekToken:=ctkUNKNOWN;
+     FPeekTokenString:='';
+     end
+  else
+    begin
+    FCurrent:=FScanner.FetchToken;
+    FCurrentTokenString:=FScanner.CurTokenString;
+    end;
+  Result:=FCurrent;
+  {$ifdef debugparser}
+     Writeln('GetNextToken returns ',
+       GetEnumName(TypeInfo(TCSSToken),Ord(FCurrent)),
+       '(String: "',FCurrentTokenString,'")',
+       ' at (',FScanner.CurRow,',',FScanner.CurColumn,'): ',
+       FSCanner.CurLine);
+  {$endif debugparser}
+end;
+
+function TCSSParser.PeekNextToken: TCSSToken;
+begin
+  If (FPeekToken=ctkUNKNOWN) then
+    begin
+    FPeekToken:=FScanner.FetchToken;
+    FPeekTokenString:=FScanner.CurTokenString;
+    end;
+  {$ifdef debugparser}Writeln('PeekNextToken : ',GetEnumName(TypeInfo(TCSSToken),Ord(FPeekToken)), ' As UTF8String: ',FPeekTokenString);{$endif debugparser}
+  Result:=FPeekToken;
+end;
+
+function TCSSParser.ParseUnit : TCSSUnits;
+
+begin
+  Result:=cuNone;
+  if (CurrentToken in [ctkIDENTIFIER,ctkPERCENTAGE]) then
+    begin
+    Case currentTokenString of
+    '%'   : Result:=cuPERCENT;
+    'px'  : Result:=cuPX;
+    'rem' : Result:=cuREM;
+    'em'  : Result:=cuEM;
+    'fr'  : Result:=cuFR;
+    'vw'  : Result:=cuVW;
+    'vh'  : Result:=cuVH;
+    'pt'  : Result:=cuPT;
+    'deg' : Result:=cuDEG;
+    else
+      // Ignore. For instance margin: 0 auto
+    end;
+    if Result<>cuNone then
+      Consume(CurrentToken);
+    end;
+end;
+
+function TCSSParser.CreateElement(aClass : TCSSElementClass): TCSSElement;
+
+begin
+  Result:=aClass.Create(CurrentSource,CurrentLine,CurrentPos);
+end;
+
+function TCSSParser.ParseIdentifier: TCSSElement;
+
+Var
+  aValue : UTF8String;
+  aId : TCSSIdentifierElement;
+
+begin
+  aValue:=CurrentTokenString;
+  if CurrentToken=ctkCLASSNAME then
+    aId:=TCSSClassNameElement(CreateElement(TCSSClassNameElement))
+  else
+    aId:=TCSSIdentifierElement(CreateElement(TCSSIdentifierElement));
+  try
+    Consume(CurrentToken);
+    aId.Value:=aValue;
+    Result:=aId;
+  except
+    aId.Free;
+    Raise;
+  end;
+end;
+
+function TCSSParser.ParseInteger: TCSSElement;
+
+Var
+  aValue : Integer;
+  aInt : TCSSIntegerElement;
+
+begin
+  aValue:=StrToInt(CurrentTokenString);
+  aInt:=TCSSIntegerElement(CreateElement(TCSSIntegerElement));
+  try
+    aInt.Value:=aValue;
+    Consume(ctkINTEGER);
+    aInt.Units:=ParseUnit;
+    result:=aInt;
+  except
+    aInt.Free;
+    Raise;
+  end;
+end;
+
+function TCSSParser.ParseFloat: TCSSElement;
+Var
+  aCode : Integer;
+  aValue : Double;
+  aFloat : TCSSFloatElement;
+
+begin
+  Val(CurrentTokenString,aValue,aCode);
+  if aCode<>0 then
+
+    DoError(SErrInvalidFloat,[CurrentTokenString]);
+  aFloat:=TCSSFloatElement(CreateElement(TCSSFloatElement));
+  try
+    Consume(ctkFloat);
+    aFloat.Value:=aValue;
+    aFloat.Units:=ParseUnit;
+    result:=aFloat;
+  except
+    aFloat.Free;
+    Raise;
+  end;
+end;
+
+
+function TCSSParser.ParseParenthesis: TCSSElement;
+
+begin
+  Consume(ctkLPARENTHESIS);
+  Result:=ParseComponentValueList;
+  try
+    Consume(ctkRPARENTHESIS);
+  except
+    Result.Free;
+    Raise;
+  end;
+end;
+
+function TCSSParser.ParseURL: TCSSElement;
+
+Var
+  aURL : TCSSURLElement;
+
+begin
+  aURL:=TCSSURLElement(CreateElement(TCSSURLElement));
+  try
+    aURL.Value:=CurrentTokenString;
+    if CurrentToken=ctkURL then
+      consume(ctkURL)
+    else
+      consume(ctkBADURL);
+     Result:=aURL;
+  except
+    aURL.Free;
+    Raise;
+  end;
+end;
+
+function TCSSParser.ParsePseudo: TCSSElement;
+
+Var
+  aPseudo : TCSSIdentifierElement;
+  aValue : string;
+
+begin
+  aValue:=CurrentTokenString;
+  aPseudo:=TCSSIdentifierElement(CreateElement(TCSSIdentifierElement));
+  try
+    Consume(ctkPseudo);
+    aPseudo.Value:=aValue;
+    Result:=aPseudo;
+  except
+    aPseudo.Free;
+    Raise;
+  end;
+end;
+
+function TCSSParser.ParseRuleBody(aRule: TCSSRuleElement; aIsAt: Boolean = false): integer;
+
+Var
+  aDecl : TCSSDeclarationElement;
+
+begin
+  if not (CurrentToken in [ctkRBRACE,ctkSEMICOLON]) then
+    begin
+    aDecl:=ParseDeclaration(aIsAt) as TCSSDeclarationElement;
+    if Assigned(aDecl) then
+      aRule.AddChild(aDecl);
+    end;
+  While Not (CurrentToken in [ctkEOF,ctkRBRACE]) do
+    begin
+    if aDecl.Colon then
+      While CurrentToken=ctkSEMICOLON do
+        Consume(ctkSEMICOLON);
+    if Not (CurrentToken in [ctkEOF,ctkRBRACE]) then
+      begin
+      aDecl:=ParseDeclaration(aIsAt);
+      if Assigned(aDecl) then
+        aRule.AddChild(aDecl);
+      end;
+    end;
+  Result:=aRule.ChildCount;
+end;
+
+function TCSSParser.ParseRule(IsAt : Boolean = False): TCSSElement;
+
+Var
+  aRule : TCSSRuleElement;
+  aSel : TCSSElement;
+  Term : TCSSTokens;
+  aLast : TCSSToken;
+  aList: TCSSListElement;
+
+begin
+//  Writeln('Parse rule. IsAt:',IsAt);
+  Term:=[ctkLBRACE,ctkEOF,ctkSEMICOLON];
+  if IsAt then
+    begin
+    aRule:=TCSSAtRuleElement(CreateElement(TCSSAtRuleElement));
+    TCSSAtRuleElement(aRule).AtKeyWord:=CurrentTokenString;
+    Consume(ctkATKEYWORD);
+    end
+  else
+    aRule:=TCSSRuleElement(CreateElement(TCSSRuleElement));
+  try
+    aList:=TCSSListElement(CreateElement(TCSSListElement));
+    While Not (CurrentToken in Term) do
+      begin
+      aSel:=ParseSelector;
+      aRule.AddSelector(aSel);
+      if CurrentToken=ctkCOMMA then
+        begin
+        Consume(ctkCOMMA);
+        aRule.AddSelector(GetAppendElement(aList));
+        aList:=TCSSListElement(CreateElement(TCSSListElement));
+        end;
+      end;
+    aRule.AddSelector(GetAppendElement(aList));
+    aLast:=CurrentToken;
+    if (aLast<>ctkSEMICOLON) then
+      begin
+      Consume(ctkLBrace);
+      ParseRuleBody(aRule,IsAt);
+      // Writeln('Parsed rule');
+      Consume(ctkRBRACE);
+      end;
+    Result:=aRule;
+  except
+    aRule.Free;
+    Raise;
+  end;
+end;
+
+function TCSSParser.ParseUnary: TCSSElement;
+
+var
+  Un : TCSSUnaryElement;
+  Op : TCSSUnaryOperation;
+
+begin
+  Result:=nil;
+  if not (CurrentToken in [ctkDOUBLECOLON, ctkMinus, ctkPlus, ctkDiv]) then
+    Raise ECSSParser.CreateFmt(SUnaryInvalidToken,[CurrentTokenString]);
+  Un:=TCSSUnaryElement(CreateElement(TCSSUnaryElement));
+  try
+    op:=TokenToUnaryOperation(CurrentToken);
+    Consume(CurrentToken);
+    Un.Operation:=op;
+    Un.Right:=ParseComponentValue;
+    Result:=un;
+  except
+    Un.Free;
+    Raise;
+  end;
+end;
+
+function TCSSParser.ParseComponentValueList(allowRules : Boolean = True): TCSSElement;
+
+Const
+  TermSeps = [ctkEquals,ctkPlus,ctkMinus,ctkAnd,ctkLT,ctkDIV,
+              ctkStar,ctkTilde,ctkColon, ctkDoubleColon,
+              ctkSquared,ctkGT];
+
+
+  function DoBinary(var aLeft : TCSSElement) : TCSSElement;
+  var
+    Bin : TCSSBinaryElement;
+  begin
+    Bin:=TCSSBinaryElement(CreateElement(TCSSBinaryElement));
+    try
+      Bin.Left:=ALeft;
+      aLeft:=Nil;
+      Bin.Operation:=TokenToBinaryOperation(CurrentToken);
+      Consume(CurrentToken);
+      Bin.Right:=ParseComponentValue;
+      Result:=Bin;
+    except
+      Bin.Free;
+      Raise;
+    end;
+  end;
+
+begin
+  Result:=Nil;
+  if not AllowRules then
+    Result:=ParseComponentValue
+  else
+    Case CurrentToken of
+      ctkLBRACE : Result:=ParseRule();
+      ctkATKEYWORD : Result:=ParseRule(True);
+    else
+      Result:=ParseComponentValue;
+    end;
+  If Not Assigned(Result) then
+    exit;
+  try
+    While CurrentToken in TermSeps do
+      Result:=DoBinary(Result);
+  except
+    Result.Free;
+    Raise;
+  end;
+end;
+
+
+function TCSSParser.ParseComponentValue: TCSSElement;
+
+Const
+  FinalTokens =
+     [ctkLPARENTHESIS,ctkURL,ctkColon,ctkLBRACE, ctkLBRACKET,
+      ctkDOUBLECOLON,ctkMinus,ctkPlus,ctkDiv,ctkSTAR,ctkTILDE];
+
+var
+  aToken : TCSSToken;
+
+begin
+  aToken:=CurrentToken;
+  Case aToken of
+    ctkLPARENTHESIS: Result:=ParseParenthesis;
+    ctkURL: Result:=ParseURL;
+    ctkPSEUDO: Result:=ParsePseudo;
+    ctkLBRACE: Result:=ParseRule;
+    ctkLBRACKET: Result:=ParseArray(Nil);
+    ctkMinus,
+    ctkPlus,
+    ctkDiv: Result:=ParseUnary;
+    ctkUnicodeRange: Result:=ParseUnicodeRange;
+    ctkSTRING,
+    ctkHASH : Result:=ParseString;
+    ctkINTEGER: Result:=ParseInteger;
+    ctkFloat : Result:=ParseFloat;
+    ctkPSEUDOFUNCTION,
+    ctkFUNCTION : Result:=ParseCall('');
+    ctkSTAR,
+    ctkTILDE,
+    ctkIDENTIFIER,
+    ctkCLASSNAME : Result:=ParseIdentifier;
+  else
+    Result:=nil;
+//    Consume(aToken);// continue
+  end;
+  if aToken in FinalTokens then
+    exit;
+  if (CurrentToken=ctkLBRACKET) then
+    Result:=ParseArray(Result);
+end;
+
+function TCSSParser.ParseSelector: TCSSElement;
+
+begin
+  Result:=ParseComponentValueList(false);
+end;
+
+function TCSSParser.ParseDeclaration(aIsAt: Boolean = false): TCSSDeclarationElement;
+
+Var
+  aDecl : TCSSDeclarationElement;
+  aKey,aValue : TCSSElement;
+  aPrevDisablePseudo : Boolean;
+  aList : TCSSListElement;
+
+begin
+  aDecl:= TCSSDeclarationElement(CreateElement(TCSSDeclarationElement));
+  try
+    aPrevDisablePseudo:= Scanner.DisablePseudo;
+    Scanner.DisablePseudo:=True;
+    aKey:=ParseComponentValue;
+    aDecl.AddKey(aKey);
+    if aIsAt then
+      begin
+      While (CurrentToken=ctkCOMMA) do
+        begin
+        while (CurrentToken=ctkCOMMA) do
+          consume(ctkCOMMA);
+        aKey:=ParseComponentValue;
+        aDecl.AddKey(aKey);
+        end;
+      end;
+    if Not aIsAt then
+      begin
+      aDecl.Colon:=True;
+      consume(ctkCOLON);
+      end
+    else
+      begin
+      aDecl.Colon:=CurrentToken=ctkColon;
+      if aDecl.Colon then
+        Consume(ctkColon)
+      end;
+    Scanner.DisablePseudo:=aPrevDisablePseudo;
+    aValue:=ParseComponentValue;
+    aList:=TCSSListElement(CreateElement(TCSSListElement));
+    aList.AddChild(aValue);
+    if aDecl.Colon then
+      begin
+      While not (CurrentToken in [ctkSemicolon,ctkRBRACE,ctkImportant]) do
+        begin
+        While CurrentToken=ctkCOMMA do
+          begin
+          Consume(ctkCOMMA);
+          aDecl.AddChild(GetAppendElement(aList));
+          aList:=TCSSListElement(CreateElement(TCSSListElement));
+          end;
+        aValue:=ParseComponentValue;
+        aList.AddChild(aValue);
+        end;
+      if CurrentToken=ctkImportant then
+        begin
+        consume(ctkImportant);
+        aDecl.IsImportant:=True;
+        end;
+      end;
+    aDecl.AddChild(GetAppendElement(aList));
+    Result:=aDecl;
+  except
+    Scanner.DisablePseudo:=False;
+    aDecl.Free;
+    Raise;
+  end;
+end;
+
+function TCSSParser.ParseCall(aName : UTF8String): TCSSElement;
+
+var
+  aCall : TCSSCallElement;
+  l : Integer;
+begin
+  aCall:=TCSSCallElement(CreateELement(TCSSCallElement));
+  try
+    if (aName='') then
+      aName:=CurrentTokenString;
+    L:=Length(aName);
+    if (L>0) and (aName[L]='(') then
+      aName:=Copy(aName,1,L-1);
+    if CurrentToken=ctkPSEUDOFUNCTION then
+      Consume(ctkPSEUDOFUNCTION)
+    else
+      Consume(ctkFUNCTION);
+    While not (CurrentToken in [ctkRPARENTHESIS,ctkEOF]) do
+      begin
+      aCall.AddArg(ParseComponentValueList);
+      if (CurrentToken=ctkCOMMA) then
+        Consume(ctkCOMMA);
+      end;
+    if CurrentToken=ctkEOF then
+      DoError(SErrUnexpectedEndOfFile,[aName]);
+    Consume(ctkRPARENTHESIS);
+    // Call argument list can be empty: mask()
+    Result:=aCall;
+  except
+    aCall.Free;
+    Raise;
+  end;
+end;
+
+
+function TCSSParser.ParseString: TCSSElement;
+
+Var
+  aValue : UTF8String;
+  aEl : TCSSElement;
+  aStr : TCSSStringElement;
+
+begin
+  aValue:=CurrentTokenString;
+  aStr:=TCSSStringElement(CreateElement(TCSSStringElement));
+  try
+    if CurrentToken=ctkSTRING then
+      Consume(ctkSTRING)
+    else
+      Consume(ctkHASH);
+    aStr.Value:=aValue;
+    While (CurrentToken in [ctkIDENTIFIER,ctkSTRING,ctkINTEGER,ctkFLOAT,ctkHASH]) do
+      begin
+      aEl:=ParseComponentValue;
+      aStr.Children.Add(aEl);
+      end;
+    Result:=aStr;
+  except
+    aStr.Free;
+    Raise;
+  end;
+end;
+
+function TCSSParser.ParseUnicodeRange: TCSSElement;
+Var
+  aValue : String;
+  aRange : TCSSUnicodeRangeElement;
+
+begin
+  aValue:=CurrentTokenString;
+  aRange:=TCSSUnicodeRangeElement(CreateElement(TCSSUnicodeRangeElement));
+  try
+    Consume(ctkUnicodeRange);
+    aRange.Value:=aValue;
+    result:=aRange;
+  except
+    aRange.Free;
+    Raise;
+  end;
+
+end;
+
+function TCSSParser.ParseArray(aPrefix: TCSSElement): TCSSElement;
+
+Var
+  aEl : TCSSElement;
+  aArray : TCSSArrayElement;
+
+begin
+  Result:=Nil;
+  aArray:=TCSSArrayElement(CreateElement(TCSSArrayElement));
+  try
+    aArray.Prefix:=aPrefix;
+    Consume(ctkLBRACKET);
+    While CurrentToken<>ctkRBRACKET do
+      begin
+      aEl:=ParseComponentValueList;
+      aArray.AddChild(aEl);
+      end;
+    Consume(ctkRBRACKET);
+    Result:=aArray;
+  except
+    aArray.Free;
+    Raise;
+  end;
+end;
+
+
+
+
+end.
+

+ 828 - 0
packages/fcl-css/src/fpcssscanner.pp

@@ -0,0 +1,828 @@
+{
+    This file is part of the Free Pascal Run time library.
+    Copyright (c) 2022- by Michael Van Canneyt ([email protected])
+
+    This file contains CSS scanner and tokenizer
+
+    See the File COPYING.FPC, included in this distribution,
+    for details about the copyright.
+
+    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.
+
+ **********************************************************************}
+
+unit fpCSSScanner;
+
+{$mode ObjFPC}{$H+}
+
+interface
+
+uses
+  Classes, SysUtils;
+
+Type
+  TCSSToken =  (
+    ctkUNKNOWN,
+    ctkEOF,
+    ctkWHITESPACE,
+    ctkCOMMENT,
+    ctkSEMICOLON,
+    ctkLPARENTHESIS,
+    ctkRPARENTHESIS,
+    ctkLBRACE,
+    ctkRBRACE,
+    ctkLBRACKET,
+    ctkRBRACKET,
+    ctkCOMMA,
+    ctkEQUALS,
+    ctkAND,
+    ctkTILDE,
+    ctkPLUS,
+    ctkCOLON,
+    ctkDOUBLECOLON,
+    ctkDOT,
+    ctkDIV,
+    ctkGT,
+    ctkLT,
+    ctkPERCENTAGE,
+    ctkMINUS,
+    ctkSTAR,
+    ctkINTEGER,
+    ctkFLOAT,
+    ctkHASH,
+    ctkSTRING,
+    ctkIDENTIFIER,
+    ctkATKEYWORD,
+    ctkURL,
+    ctkBADURL,
+    ctkIMPORTANT,
+    ctkCLASSNAME,
+    ctkFUNCTION,
+    ctkPSEUDO,
+    ctkPSEUDOFUNCTION,
+    ctkSQUARED,
+    ctkUNICODERANGE
+   );
+  TCSSTokens = Set of TCSSToken;
+
+resourcestring
+  SErrInvalidCharacter = 'Invalid character ''%s''';
+  SErrOpenString = 'UTF8String exceeds end of line';
+  SErrIncludeFileNotFound = 'Could not find include file ''%s''';
+  SInvalidHexadecimalNumber = 'Invalid decimal number';
+  SErrUnknownCharacter = 'Unknown character: %s';
+
+Type
+  ECSSScanner = Class(Exception);
+
+  TLineReader = class
+  public
+    function IsEOF: Boolean; virtual; abstract;
+    function ReadLine: UTF8String; virtual; abstract;
+  end;
+
+  { TStreamLineReader }
+
+  TStreamLineReader = class(TLineReader)
+  private
+    FStream : TStream;
+    Buffer : Array[0..1024] of Byte;
+    FBufPos,
+    FBufLen : Integer;
+    procedure FillBuffer;
+  public
+    Constructor Create(AStream : TStream);
+    function IsEOF: Boolean; override;
+    function ReadLine: UTF8String; override;
+  end;
+
+  TFileLineReader = class(TLineReader)
+  private
+    FTextFile: Text;
+    FileOpened: Boolean;
+  public
+    constructor Create(const AFilename: UTF8String);
+    destructor Destroy; override;
+    function IsEOF: Boolean; override;
+    function ReadLine: UTF8String; override;
+  end;
+
+  { TCSSScanner }
+
+
+  TCSSScanner = class
+  private
+    FDisablePseudo: Boolean;
+    FReturnComments: Boolean;
+    FReturnWhiteSpace: Boolean;
+    FSourceFile: TLineReader;
+    FSourceFilename: UTF8String;
+    FCurRow: Integer;
+    FCurToken: TCSSToken;
+    FCurTokenString: UTF8String;
+    FCurLine: UTF8String;
+    TokenStr: PChar;
+    FSourceStream : TStream;
+    FOwnSourceFile : Boolean;
+    function DoHash: TCSSToken;
+    function DoIdentifierLike : TCSSToken;
+    function DoMultiLineComment: TCSSToken;
+    function CommentDiv: TCSSToken;
+    function DoNumericLiteral: TCSSToken;
+    function DoSingleLineComment: TCSSToken;
+    function DoStringLiteral: TCSSToken;
+    function DoWhiteSpace: TCSSToken;
+    function EatBadURL: TCSSToken;
+    Function DoUnicodeRange : TCSSTOKEN;
+    function FetchLine: Boolean;
+    function GetCurColumn: Integer;
+    function ReadUnicodeEscape: WideChar;
+  protected
+    procedure DoError(const Msg: UTF8String; Args: array of const); overload;
+    procedure DoError(const Msg: UTF8String); overload;
+    function DoFetchToken: TCSSToken; virtual;
+  public
+    constructor Create(ALineReader: TLineReader);
+    constructor Create(AStream : TStream);
+    destructor Destroy; override;
+    procedure OpenFile(const AFilename: UTF8String);
+    Function FetchToken: TCSSToken;
+    Property ReturnComments : Boolean Read FReturnComments Write FReturnComments;
+    Property ReturnWhiteSpace : Boolean Read FReturnWhiteSpace Write FReturnWhiteSpace;
+    property SourceFile: TLineReader read FSourceFile;
+    property CurFilename: UTF8String read FSourceFilename;
+    property CurLine: UTF8String read FCurLine;
+    property CurRow: Integer read FCurRow;
+    property CurColumn: Integer read GetCurColumn;
+    property CurToken: TCSSToken read FCurToken;
+    property CurTokenString: UTF8String read FCurTokenString;
+    Property DisablePseudo : Boolean Read FDisablePseudo Write FDisablePseudo;
+  end;
+
+
+implementation
+
+Const
+  Alpha = ['A'..'Z','a'..'z'];
+  Num   = ['0'..'9'];
+  AlNum = Alpha+Num;
+  AlNumIden = Alpha+Num+['-'];
+  WhiteSpace = [' ',#8];
+  WhiteSpaceEx = WhiteSpace+[#0];
+
+
+constructor TFileLineReader.Create(const AFilename: UTF8String);
+begin
+  inherited Create;
+  Assign(FTextFile, AFilename);
+  Reset(FTextFile);
+  FileOpened := true;
+end;
+
+destructor TFileLineReader.Destroy;
+begin
+  if FileOpened then
+    Close(FTextFile);
+  inherited Destroy;
+end;
+
+function TFileLineReader.IsEOF: Boolean;
+begin
+  Result := EOF(FTextFile);
+end;
+
+function TFileLineReader.ReadLine: UTF8String;
+begin
+  ReadLn(FTextFile, Result);
+end;
+
+constructor TCSSScanner.Create(ALineReader: TLineReader);
+begin
+  inherited Create;
+  FSourceFile := ALineReader;
+end;
+
+constructor TCSSScanner.Create(AStream: TStream);
+begin
+  FSourceStream:=ASTream;
+  FOwnSourceFile:=True;
+  Create(TStreamLineReader.Create(AStream));
+end;
+
+destructor TCSSScanner.Destroy;
+begin
+  If FOwnSourceFile then
+    FSourceFile.Free;
+  inherited Destroy;
+end;
+
+procedure TCSSScanner.OpenFile(const AFilename: UTF8String);
+begin
+  FSourceFile := TFileLineReader.Create(AFilename);
+  FSourceFilename := AFilename;
+end;
+
+
+
+function TCSSScanner.FetchLine: Boolean;
+begin
+  if FSourceFile.IsEOF then
+  begin
+    FCurLine := '';
+    TokenStr := nil;
+    Result := false;
+  end else
+  begin
+    FCurLine := FSourceFile.ReadLine;
+    TokenStr := PChar(CurLine);
+    Result := true;
+    Inc(FCurRow);
+  end;
+end;
+
+function TCSSScanner.DoWhiteSpace : TCSSToken;
+
+begin
+  Result:=ctkWhitespace;
+  repeat
+    Inc(TokenStr);
+    if TokenStr[0] = #0 then
+      if not FetchLine then
+       begin
+       FCurToken := Result;
+       exit;
+       end;
+  until not (TokenStr[0] in [#9, ' ']);
+end;
+
+function TCSSScanner.DoSingleLineComment : TCSSToken;
+
+Var
+  TokenStart : PChar;
+  Len : Integer;
+
+begin
+  Inc(TokenStr);
+  TokenStart := TokenStr;
+  while TokenStr[0] <> #0 do
+     Inc(TokenStr);
+  Len:=TokenStr-TokenStart;
+  SetLength(FCurTokenString, Len);
+  if (Len>0) then
+    Move(TokenStart^,FCurTokenString[1],Len);
+  Result := ctkComment;
+end;
+
+function TCSSScanner.DoMultiLineComment : TCSSToken;
+
+Var
+  TokenStart : PChar;
+  Len,OLen : Integer;
+  PrevToken : Char;
+
+begin
+  Inc(TokenStr);
+  TokenStart := TokenStr;
+  FCurTokenString := '';
+  OLen:= 0;
+  PrevToken:=#0;
+  while Not ((TokenStr[0]='/') and (PrevToken='*')) do
+    begin
+    if (TokenStr[0]=#0) then
+      begin
+      Len:=TokenStr-TokenStart+1;
+      SetLength(FCurTokenString,OLen+Len);
+      if Len>1 then
+        Move(TokenStart^,FCurTokenString[OLen+1],Len-1);
+      Inc(OLen,Len);
+      FCurTokenString[OLen]:=#10;
+      if not FetchLine then
+        begin
+        Result := ctkEOF;
+        FCurToken := Result;
+        exit;
+        end;
+      TokenStart := TokenStr;
+      PrevToken:=#0;
+      end
+    else
+      begin
+      PrevToken:=TokenStr[0];
+      Inc(TokenStr);
+      end;
+    end;
+  Len:=TokenStr-TokenStart-1; // -1 for *
+  SetLength(FCurTokenString, Olen+Len);
+  if (Len>0) then
+    Move(TokenStart^, FCurTokenString[Olen + 1], Len);
+  Inc(TokenStr);
+  Result := ctkComment;
+end;
+
+function TCSSScanner.CommentDiv : TCSSToken;
+
+begin
+  FCurTokenString := '';
+  Inc(TokenStr);
+  if (TokenStr[0] = '/') then       // Single-line comment
+    Result:=DoSingleLineComment
+  else if (TokenStr[0]='*') then
+    Result:=DoMultiLineComment
+  else
+    Result:=ctkDiv;
+end;
+
+function TCSSScanner.ReadUnicodeEscape: WideChar;
+
+const
+  Hex = ['0'..'9','A'..'F','a'..'f' ];
+
+Var
+  S : UTF8String;
+  I : Integer;
+  HaveHex : Boolean;
+
+begin
+  S:='';
+  I:=1;
+  Repeat
+    S:=S+Upcase(TokenStr[0]);
+    HaveHex:=TokenStr[1] in Hex;
+    if HaveHex then
+      Inc(TokenStr);
+    Inc(I);
+  Until (I>4) or not HaveHex;
+  // Takes care of conversion... This needs improvement !!
+  Result:=WideChar(StrToInt('$'+S));
+end;
+
+
+function TCSSScanner.DoStringLiteral: TCSSToken;
+
+Var
+  Delim : Char;
+  TokenStart : PChar;
+  Len,OLen: Integer;
+  S : UTF8String;
+
+begin
+  Delim:=TokenStr[0];
+  Inc(TokenStr);
+  TokenStart := TokenStr;
+  OLen := 0;
+  FCurTokenString := '';
+  while not (TokenStr[0] in [#0,Delim]) do
+    begin
+    if (TokenStr[0]='\') then
+      begin
+      // Save length
+      Len := TokenStr - TokenStart;
+      Inc(TokenStr);
+      // Read escaped token
+      Case TokenStr[0] of
+        '"' : S:='"';
+        'a'..'f',
+        'A'..'F',
+        '0'..'9':
+              begin
+              S:=UTF8Encode(ReadUniCodeEscape);
+              end;
+        #0  : DoError(SErrOpenString);
+      else
+        DoError(SErrInvalidCharacter, [TokenStr[0]]);
+      end;
+      SetLength(FCurTokenString, OLen + Len+1+Length(S));
+      if Len > 0 then
+        Move(TokenStart^, FCurTokenString[OLen + 1], Len);
+      Move(S[1],FCurTokenString[OLen + Len+1],Length(S));
+      Inc(OLen, Len+Length(S));
+      // Next char
+      // Inc(TokenStr);
+      TokenStart := TokenStr+1;
+      end;
+    if TokenStr[0] = #0 then
+      DoError(SErrOpenString);
+    Inc(TokenStr);
+    end;
+  if TokenStr[0] = #0 then
+    DoError(SErrOpenString);
+  Len := TokenStr - TokenStart;
+  SetLength(FCurTokenString, OLen + Len);
+  if Len > 0 then
+    Move(TokenStart^, FCurTokenString[OLen+1], Len);
+  Inc(TokenStr);
+  Result := ctkSTRING;
+end;
+
+function TCSSScanner.DoNumericLiteral :TCSSToken;
+
+Var
+  TokenStart : PChar;
+  Len : Integer;
+
+begin
+  Result := ctkINTEGER;
+  TokenStart := TokenStr;
+  while true do
+    begin
+    Inc(TokenStr);
+    case TokenStr[0] of
+      '.':
+        begin
+          Result := ctkFLOAT;
+          if TokenStr[1] in ['0'..'9'] then
+          begin
+            Inc(TokenStr);
+            repeat
+              Inc(TokenStr);
+            until not (TokenStr[0] in ['0'..'9']);
+          end;
+          break;
+        end;
+      '0'..'9': ;
+      else
+        break;
+    end;
+  end;
+  Len:=TokenStr-TokenStart;
+  Setlength(FCurTokenString, Len);
+  if (Len>0) then
+  Move(TokenStart^,FCurTokenString[1],Len);
+end;
+
+function TCSSScanner.DoHash :TCSSToken;
+
+Var
+  TokenStart : PChar;
+  Len : Integer;
+
+begin
+  Result := ctkHASH;
+  TokenStart := TokenStr;
+  Inc(TokenStr);
+  while (TokenStr[0]<>'#') and (TokenStr[0] in AlNumIden) do
+    inc(TokenStr);
+  Len:=TokenStr-TokenStart;
+  Setlength(FCurTokenString, Len);
+  if (Len>0) then
+  Move(TokenStart^,FCurTokenString[1],Len);
+end;
+
+
+function TCSSScanner.EatBadURL: TCSSToken;
+
+var
+  TokenStart : PChar;
+  C : Char;
+  len,oldlen : integer;
+
+begin
+  Result:=ctkURL;
+  While not (TokenStr[0] in [#0,')']) do
+    begin
+    TokenStart:=TokenStr;
+    While not (TokenStr[0] in [#0,')']) do
+      begin
+      C:=TokenStr[0];
+      if (Ord(C)<=Ord(' ')) or (Ord(C)>127) then
+        Result:=ctkBADURL;
+      inc(TokenStr);
+      end;
+    Len:=TokenStr-TokenStart;
+    oldLen:=Length(FCurTokenString);
+    Setlength(FCurTokenString, OldLen+Len);
+    if (Len>0) then
+      Move(TokenStart^,FCurTokenString[OldLen+1],Len);
+    if TokenStr[0]=#0 then
+      if not FetchLine then
+        Exit(ctkEOF);
+    end;
+end;
+
+function TCSSScanner.DoUnicodeRange: TCSSTOKEN;
+Var
+  TokenStart:PChar;
+  Len : Integer;
+  Tokens : Set of char;
+
+begin
+  Tokens:= ['A'..'F', 'a'..'f', '0'..'9', '-'];
+  Result:=ctkUNICODERANGE;
+  TokenStart := TokenStr;
+  Inc(TokenStr,2); // U+
+  repeat
+    if (TokenStr[0]='-') then
+      Tokens:=Tokens-['-'];
+    Inc(TokenStr);
+    //If (TokenStr[0]='\') and (TokenStr[1]='u') then
+  until not (TokenStr[0] in Tokens);
+  Len:=(TokenStr-TokenStart);
+  SetLength(FCurTokenString,Len);
+  if Len > 0 then
+    Move(TokenStart^,FCurTokenString[1],Len);
+
+end;
+
+function TCSSScanner.DoIdentifierLike : TCSSToken;
+
+Var
+  TokenStart:PChar;
+  Len : Integer;
+  IsAt, IsPseudo, IsFunc : Boolean;
+
+
+begin
+  Result:=ctkIDENTIFIER;
+  TokenStart := TokenStr;
+  IsPseudo:=False;
+  IsAt:=TokenStr[0]='@';
+  For Len:=1 to 2 do
+    if TokenStr[0]=':' then
+      begin
+      IsPseudo:=True;
+      Inc(TokenStr);
+      end;
+  repeat
+    Inc(TokenStr);
+    //If (TokenStr[0]='\') and (TokenStr[1]='u') then
+  until not (TokenStr[0] in ['A'..'Z', 'a'..'z', '0'..'9', '_','-','$']);
+  if not IsAt then
+    begin
+    IsFunc:=TokenStr[0]='(';
+    if IsFunc then
+      Inc(TokenStr);
+    end;
+  Len:=(TokenStr-TokenStart);
+  SetLength(FCurTokenString,Len);
+  if Len > 0 then
+    Move(TokenStart^,FCurTokenString[1],Len);
+  // Some specials
+  if (CurTokenString[1]='.') and not IsFunc then
+    Result:=ctkCLASSNAME
+  else if isAt then
+    Result:=ctkATKEYWORD
+  else if CurTokenString='!important' then
+    Result:=ctkIMPORTANT
+  else if (CurtokenString='url(') then
+    begin
+    Result:=ctkURL;
+    If TokenStr[0] in ['"',''''] then
+      DoStringLiteral
+    else
+      begin
+      result:=EatBadURL;
+      end;
+    If (result<>ctkEOF) and (TokenStr[0] in [')']) then
+      Inc(TokenStr);
+    end
+  else if IsPseudo then
+    begin
+    if IsFunc then
+      Result:=ctkPSEUDOFUNCTION
+    else
+      Result:=ctkPSEUDO;
+    end
+  else if IsFunc then
+    Result:=ctkFUNCTION;
+end;
+
+function TCSSScanner.FetchToken: TCSSToken;
+
+var
+  CanStop : Boolean;
+
+begin
+  Repeat
+    Result:=DoFetchToken;
+    CanStop:=(Not (Result in [ctkComment,ctkWhiteSpace]))
+             or ((ReturnComments and (Result=ctkComment))
+                  or
+                 (ReturnWhiteSpace and (Result=ctkWhiteSpace))
+                )
+  Until CanStop;
+end;
+
+function TCSSScanner.DoFetchToken: TCSSToken;
+
+
+  Procedure CharToken(aToken : TCSSToken); inline;
+
+  begin
+    FCurTokenString:=TokenStr[0];
+    Inc(TokenStr);
+    Result:=aToken;
+  end;
+
+begin
+  if TokenStr = nil then
+    begin
+    if not FetchLine then
+      begin
+      Result := ctkEOF;
+      FCurToken := Result;
+      exit;
+      end;
+    end;
+  //CurPos:=TokenStr;
+  FCurTokenString := '';
+  case TokenStr[0] of
+    #0:         // Empty line
+      begin
+      FetchLine;
+      Result := ctkWhitespace;
+      end;
+    '''','"':
+       Result:=DoStringLiteral;
+    '/' :
+       Result:=CommentDiv;
+    #9, ' ':
+       Result := DoWhiteSpace;
+    '#':
+       Result:=DoHash;
+    '\':
+       begin
+       if TokenStr[0] in ['0'..'9'] then
+         Result:=DoNumericLiteral
+       else
+         begin
+         Inc(TokenStr);
+         if TokenStr[0]=#0 then
+           Result:=ctkEOF
+         else
+           CharToken(ctkSTRING);
+         end;
+       end;
+    '0'..'9':
+       Result:=DoNumericLiteral;
+    '&': CharToken(ctkAnd);
+    '{': CharToken(ctkLBRACE);
+    '}': CharToken(ctkRBRACE);
+    '*': CharToken(ctkSTAR);
+    '^': CharToken(ctkSQUARED);
+    ',': CharToken(ctkCOMMA);
+    '~': CharToken(ctkTILDE);
+    ';': CharToken(ctkSEMICOLON);
+    '@': Result:=DoIdentifierLike;
+    ':':
+      begin
+      if DisablePseudo then
+        CharToken(ctkCOLON)
+      else if (TokenStr[1]=':') then
+        begin
+        if (TokenStr[2] in AlNumIden) then
+          Result:=DoIdentifierLike
+        else
+          Result:=ctkDoubleCOLON
+        end
+      else if (TokenStr[1] in AlNumIden) then
+        Result:=DoIdentifierLike
+      else
+        CharToken(ctkCOLON);
+      end;
+    '.':
+      begin
+      if (TokenStr[1] in AlNum) then
+        Result:=Self.DoIdentifierLike
+      else
+        CharToken(ctkDOT);
+      end;
+    '>': CharToken(ctkGT);
+    '<': CharToken(ctkLT);
+    '(': CharToken(ctkLPARENTHESIS);
+    ')': CharToken(ctkRPARENTHESIS);
+    '[': CharToken(ctkLBRACKET);
+    ']': CharToken(ctkRBRACKET);
+    '=': CharToken(ctkEQUALS);
+    '-':
+      begin
+      if (TokenStr[1] in ['0'..'9']) then
+        Result:=DoNumericLiteral
+      else if Not (TokenStr[1] in WhiteSpaceEx) then
+        Result:=DoIdentifierLike
+      else
+        CharToken(ctkMINUS);
+      end;
+    '+': CharToken(ctkPLUS);
+    '%': CharToken(ctkPERCENTAGE);
+    '_','!',
+    'a'..'z',
+    'A'..'Z':
+       begin
+       if (TokenStr[0] in ['u','U']) and (TokenStr[1]='+') then
+         Result:=DoUnicodeRange
+       else
+         Result:=DoIdentifierLike;
+       end;
+  else
+    If Ord(TokenStr[0])>127 then
+      Result:=DoIdentifierLike
+    else
+      DoError(SErrUnknownCharacter ,['"'+TokenStr[0]+'"']);
+
+  end; // Case
+end;
+
+procedure TCSSScanner.DoError(const Msg: UTF8String; Args: array of const);
+begin
+  DoError(Format(Msg,Args));
+end;
+
+procedure TCSSScanner.DoError(const Msg: UTF8String);
+
+Var
+  S : UTF8String;
+
+begin
+  S:=Format('Error at (%d,%d): ',[CurRow,CurColumn])+Msg;
+  Raise ECSSScanner.Create(S);
+end;
+
+function TCSSScanner.GetCurColumn: Integer;
+begin
+  if (TokenStr=Nil) or (Length(CurLine)=0) then
+    Result:=0
+  else
+    Result := TokenStr - PChar(CurLine);
+end;
+
+{ TStreamLineReader }
+
+constructor TStreamLineReader.Create(AStream: TStream);
+begin
+  FStream:=AStream;
+  FBufPos:=0;
+  FBufLen:=0;
+end;
+
+function TStreamLineReader.IsEOF: Boolean;
+begin
+  Result:=(FBufPos>=FBufLen);
+  If Result then
+    begin
+    FillBuffer;
+    Result:=(FBufLen=0);
+    end;
+end;
+
+procedure TStreamLineReader.FillBuffer;
+
+begin
+  FBufLen:=FStream.Read(Buffer,SizeOf(Buffer)-1);
+  Buffer[FBufLen]:=0;
+  FBufPos:=0;
+end;
+
+function TStreamLineReader.ReadLine: UTF8String;
+
+Var
+  FPos,OLen,Len: Integer;
+  PRun : PByte;
+
+begin
+  Result:='';
+  FPos:=FBufPos;
+  Repeat
+    PRun:=@Buffer[FBufPos];
+    While (FBufPos<FBufLen) and Not (PRun^ in [10,13]) do
+      begin
+      Inc(PRun);
+      Inc(FBufPos);
+      end;
+    If (FBufPos=FBufLen) then
+      begin
+      Len:=FBufPos-FPos;
+      If (Len>0) then
+        begin
+        Olen:=Length(Result);
+        SetLength(Result,OLen+Len);
+        Move(Buffer[FPos],Result[OLen+1],Len);
+        end;
+      FillBuffer;
+      FPos:=FBufPos;
+      end;
+  until (FBufPos=FBufLen) or (PRun^ in [10,13]);
+  Len:=FBufPos-FPos;
+  If (Len>0) then
+    begin
+    Olen:=Length(Result);
+    SetLength(Result,OLen+Len);
+    Move(Buffer[FPos],Result[OLen+1],Len)
+    end;
+  If (PRun^ in [10,13]) and (FBufPos<FBufLen) then
+    begin
+    Inc(FBufPos);
+    // Check #13#10
+    If (PRun^=13) then
+      begin
+      If (FBufPos=FBufLen) then
+        FillBuffer;
+      If (FBufPos<FBufLen) and (Buffer[FBufpos]=10) then
+        Inc(FBufPos);
+      end;
+    end;
+end;
+
+end.
+

+ 1068 - 0
packages/fcl-css/src/fpcsstree.pp

@@ -0,0 +1,1068 @@
+{
+    This file is part of the Free Pascal Run time library.
+    Copyright (c) 2022- by Michael Van Canneyt ([email protected])
+
+    This file contains the implementation of objects representing a CSS AST
+
+    See the File COPYING.FPC, included in this distribution,
+    for details about the copyright.
+
+    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.
+
+ **********************************************************************}
+
+unit fpCSSTree;
+
+{$mode ObjFPC}{$H+}
+
+interface
+
+uses contnrs;
+
+
+Type
+  TCSSUnits = (cuNONE, cuPX,cuPERCENT,cuREM,cuEM,cuPT,cuFR,cuVW,cuVH,cuDEG);
+  TCSSType = (csstUNKNOWN, csstINTEGER, csstSTRING, csstFLOAT,
+              csstIDENTIFIER, csstCLASSNAME, csstPSEUDOCLASS, csstCOMPOUND, csstRULE,
+              csstDECLARATION, csstBINARYOP, csstCALL, csstUNARYOP, csstARRAY, csstURL,
+              csstUNICODERANGE,csstLIST);
+
+  TCSSElement = class;
+
+  TCSSTreeVisitor = class
+  public
+    procedure Visit(obj: TCSSElement); virtual; abstract;
+  end;
+
+  { TCSSElement }
+
+  TCSSElement = Class(TObject)
+  private
+    FCol: Integer;
+    FData: TObject;
+    FFileName: UTF8String;
+    FRow: Integer;
+    function GetAsUnFormattedString: UTF8String;
+    function GetAsFormattedString: UTF8String;
+  Protected
+    function GetAsString(aFormat : Boolean; const aIndent : String): UTF8String; virtual;
+    procedure IterateChildren(aVisitor : TCSSTreeVisitor); virtual;
+  Public
+    Constructor Create(const aFileName : UTF8String; aRow,aCol : Integer); virtual;
+    Class function CSSType : TCSSType; virtual;
+    Procedure Iterate(aVisitor : TCSSTreeVisitor);
+    Property CustomData : TObject Read FData Write FData;
+    Property SourceRow : Integer Read FRow;
+    Property SourceCol : Integer Read FCol;
+    Property SourceFileName : UTF8String Read FFileName;
+    Property AsFormattedString : UTF8String Read GetAsFormattedString;
+    Property AsString : UTF8String Read GetAsUnformattedString;
+  end;
+  TCSSElementClass = Class of TCSSElement;
+  TCSSElementArray = Array of TCSSElement;
+
+  { TCSSElementList }
+
+  TCSSElementList = Class(TFPObjectList)
+  private
+    function GetElement(aIndex : Integer): TCSSElement;
+  Public
+    Procedure Iterate(aVisitor : TCSSTreeVisitor);
+    Property Elements[aIndex : Integer] : TCSSElement Read GetElement; default;
+  end;
+
+  { TCSSIntegerElement }
+
+  TCSSIntegerElement = class(TCSSElement)
+  private
+    FisEscaped: Boolean;
+    FUnits: TCSSUnits;
+    FValue: Integer;
+  protected
+    function GetAsString(aFormat : Boolean; const aIndent : String): UTF8String; override;
+  Public
+    Class function CSSType : TCSSType; override;
+    Property Value : Integer Read FValue Write FValue;
+    Property IsEscaped : Boolean Read FisEscaped Write FIsEscaped;
+    Property Units : TCSSUnits Read FUnits Write FUnits;
+  end;
+
+  { TCSSIntegerElement }
+
+  { TCSSFloatElement }
+
+  TCSSFloatElement = class(TCSSElement)
+  private
+    FUnits: TCSSUnits;
+    FValue: Double;
+  protected
+    function GetAsString(aFormat : Boolean; const aIndent : String): UTF8String;override;
+  Public
+    Class function CSSType : TCSSType; override;
+    Property Value : Double Read FValue Write FValue;
+    Property Units : TCSSUnits Read FUnits Write FUnits;
+  end;
+
+
+  { TCSSBaseUnaryElement }
+
+  TCSSBaseUnaryElement = Class(TCSSElement)
+  private
+    FRight: TCSSElement;
+    procedure SetRight(AValue: TCSSElement);
+  protected
+    Procedure IterateChildren(aVisitor : TCSSTreeVisitor); override;
+  Public
+    Destructor Destroy; override;
+    Property Right : TCSSElement Read FRight Write SetRight;
+  end;
+
+  { TCSSUnaryElement }
+  TCSSUnaryOperation = (uoDoubleColon,uoMinus,uoPlus,uoDiv);
+  TCSSUnaryElement = Class(TCSSBaseUnaryElement)
+  private
+    FOperation: TCSSUnaryOperation;
+  Protected
+    function GetAsString(aFormat : Boolean; const aIndent : String): UTF8String; override;
+  Public
+    Class function CSSType : TCSSType; override;
+    Property Operation : TCSSUnaryOperation Read FOperation Write FOperation;
+  end;
+
+  { TCSSBinaryElement }
+  TCSSBinaryOperation = (boEquals,boPlus,boMinus,boAnd,boLT,boGT,boDIV,
+                         boStar,boTilde,boColon, boDoubleColon,boSquared);
+  TCSSBinaryElement = Class(TCSSBaseUnaryElement)
+  private
+    FLeft: TCSSElement;
+    FOperation: TCSSBinaryOperation;
+    procedure SetLeft(AValue: TCSSElement);
+  Protected
+    function GetAsString(aFormat : Boolean; const aIndent : String): UTF8String;override;
+    procedure IterateChildren(aVisitor: TCSSTreeVisitor); override;
+  Public
+    Destructor Destroy; override;
+    Class function CSSType : TCSSType; override;
+    Property Left : TCSSElement Read FLeft Write SetLeft;
+    Property Operation : TCSSBinaryOperation Read FOperation Write FOperation;
+  end;
+
+
+  { TCSSStringElement }
+
+  { TCSSBaseStringElement }
+
+  TCSSBaseStringElement = Class(TCSSElement)
+  private
+    FValue: UTF8String;
+  Protected
+    function GetAsString(aFormat : Boolean; const aIndent : String): UTF8String; override;
+  Public
+    Property Value : UTF8String Read FValue Write FValue;
+  end;
+
+  { TCSSUnicodeRangeElement }
+
+  TCSSUnicodeRangeElement = class(TCSSBaseStringElement)
+  Public
+    Class function CSSType : TCSSType; override;
+  end;
+
+  { TCSSURLElement }
+
+  TCSSURLElement = Class(TCSSBaseStringElement)
+    Class function CSSType : TCSSType; override;
+  end;
+
+  TCSSStringElement = Class(TCSSElement)
+  private
+    FChildren : TCSSElementList;
+    FValue: UTF8String;
+    function GetChildren: TCSSElementList;
+  protected
+    function GetAsString(aFormat : Boolean; const aIndent : String): UTF8String; override;
+    procedure IterateChildren(aVisitor : TCSSTreeVisitor); override;
+  Public
+    Class function CSSType : TCSSType; override;
+    Destructor Destroy; override;
+    Property Children : TCSSElementList Read GetChildren;
+    Property Value : UTF8String Read FValue Write FValue;
+  end;
+
+  { TCSSIdentifierElement }
+
+  TCSSIdentifierElement = Class(TCSSBaseStringElement)
+  private
+    function GetName: UTF8String;
+  Protected
+    function GetAsString(aFormat : Boolean; const aIndent : String): UTF8String; override;
+  Public
+    Class function CSSType : TCSSType; override;
+    Property Name : UTF8String Read GetName;
+  end;
+
+  { TCSSClassNameElement }
+
+  TCSSClassNameElement = Class(TCSSIdentifierElement)
+  Public
+    Class function CSSType : TCSSType; override;
+  end;
+
+  { TCSSPseudoClassElement }
+
+  TCSSPseudoClassElement = Class(TCSSElement)
+  private
+    FElement: TCSSElement;
+    procedure SetElement(AValue: TCSSElement);
+  Public
+    Class function CSSType : TCSSType; override;
+    Destructor Destroy; override;
+    Property Element : TCSSElement Read FElement Write SetElement;
+  end;
+
+
+  { TCSSChildrenElement }
+
+  TCSSChildrenElement = Class(TCSSElement)
+  private
+    FChildren : TCSSElementList;
+    function GetChild(aIndex : Integer): TCSSElement;
+    function GetChildCount: Integer;
+  Protected
+    procedure IterateChildren(aVisitor : TCSSTreeVisitor); override;
+  Public
+    Destructor Destroy; override;
+    Procedure AddChild(aChild : TCSSElement); virtual;
+    Property Children[aIndex : Integer] : TCSSElement Read GetChild;
+    Property ChildCount : Integer Read GetChildCount;
+  end;
+
+  { TCSSArrayElement }
+
+  TCSSArrayElement = Class(TCSSChildrenElement)
+  private
+    FPrefix : TCSSElement;
+    procedure SetPrefix(AValue: TCSSElement);
+  Protected
+    function GetAsString(aFormat : Boolean; const aIndent : String): UTF8String; override;
+  Public
+    Destructor Destroy; override;
+    Class function CSSType : TCSSType; override;
+    Property Prefix : TCSSElement Read FPrefix Write SetPrefix;
+  end;
+
+  { TCSSCallElement }
+
+  TCSSCallElement = Class(TCSSChildrenElement)
+  private
+    FName: UTF8String;
+    function GetArg(aIndex : Integer): TCSSElement;
+    function GetArgCount: Integer;
+  Protected
+    function GetAsString(aFormat : Boolean; const aIndent : String): UTF8String; override;
+  Public
+    Class function CSSType : TCSSType; override;
+    Procedure AddArg(aArg : TCSSElement); virtual;
+    Property Args[aIndex : Integer] : TCSSElement Read GetArg; default;
+    Property ArgCount : Integer Read GetArgCount;
+    Property Name : UTF8String Read FName Write FName;
+  end;
+
+  { TCSSDeclarationElement }
+
+  TCSSDeclarationElement = class(TCSSChildrenElement)
+  private
+    FIsImportant: Boolean;
+    FKeys : TCSSElementList;
+    FColon: Boolean;
+    function GetKeyCount: Integer;
+    function GetKeys(aIndex : Integer): TCSSElement;
+  Protected
+    function GetAsString(aFormat : Boolean; const aIndent : String): UTF8String;override;
+    procedure IterateChildren(aVisitor : TCSSTreeVisitor); override;
+  Public
+    Class function CSSType : TCSSType; override;
+    Destructor Destroy; override;
+    Procedure AddKey(aKey : TCSSElement); virtual;
+    Property Keys [aIndex : Integer] : TCSSElement Read GetKeys;
+    Property KeyCount : Integer Read GetKeyCount;
+    Property IsImportant : Boolean Read FIsImportant Write FIsImportant;
+    Property Colon : Boolean Read FColon Write FColon;
+  end;
+
+  { TCSSListElement }
+
+  TCSSListElement = class(TCSSChildrenElement)
+  Protected
+    function GetAsString(aFormat : Boolean; const aIndent : String): UTF8String;override;
+  Public
+    Function ExtractElement(aIndex : Integer) : TCSSElement;
+  end;
+
+  { TCSSCompoundElement }
+
+  TCSSCompoundElement = Class(TCSSChildrenElement)
+  Protected
+    function GetAsString(aFormat : Boolean; const aIndent : String): UTF8String;override;
+  Public
+    Class function CSSType : TCSSType; override;
+  end;
+
+  { TCSSRuleElement }
+
+  TCSSRuleElement = class(TCSSChildrenElement)
+  Private
+    FSelectors : TCSSElementList;
+    function GetSelector(aIndex : Integer): TCSSElement;
+    function GetSelectorCount: Integer;
+  Protected
+    function DoGetAsString(const aPrefix : String; aFormat : Boolean; const aIndent : String): UTF8String; virtual;
+    function GetAsString(aFormat : Boolean; const aIndent : String): UTF8String;override;
+    procedure IterateChildren(aVisitor : TCSSTreeVisitor); override;
+  Public
+    Class function CSSType : TCSSType; override;
+    Destructor Destroy; override;
+    Procedure AddSelector(aSelector : TCSSElement);
+    Property Selectors [aIndex : Integer] : TCSSElement Read GetSelector;
+    Property SelectorCount : Integer Read GetSelectorCount;
+  end;
+
+  { TCSSAtRuleElement }
+
+  TCSSAtRuleElement = class(TCSSRuleElement)
+  private
+    FAtKeyWord: String;
+  Public
+    function GetAsString(aFormat : Boolean; const aIndent : String): UTF8String;override;
+    Property AtKeyWord : String Read FAtKeyWord Write FAtKeyWord;
+  end;
+
+
+
+Function StringToCSSString(S : UTF8String) : UTF8String;
+
+Const
+  CSSUnitNames : Array[TCSSUnits] of string =
+        ('','px','%','rem','em','pt','fr','vw','vh','deg');
+  UnaryOperators : Array[TCSSUnaryOperation] of string =
+        ('::','-','+','/');
+  BinaryOperators : Array[TCSSBinaryOperation] of string =
+        ('=','+','-','and','<','>','/','*','~',':','::','^');
+
+
+implementation
+
+uses SysUtils, Classes, rtlConsts, fpjson;
+
+Const
+  sIndent = '  ';
+
+Function  u8length(s : char) : Byte;
+
+const u8_length : Array[0..15] of byte = (
+// 0 1 2 3 4 5 6 7 8 9 A B C D E F
+   1,1,1,1,1,1,1,1,0,0,0,0,2,2,3,4
+) ;
+
+begin
+ Result:=u8_length[Ord(S) shr 4];
+end;
+
+function StringToCSSString(S: UTF8String): UTF8String;
+
+Var
+  iIn,iOut,I : Integer;
+  O : String[5];
+  u : UTF8String;
+  W : widestring;
+  C : Char;
+
+  Procedure AddO;
+  var
+    J : Integer;
+
+  begin
+    For J:=1 to Length(O) do
+      begin
+      Inc(iOut);
+      Result[iOut]:=O[J];
+      end;
+  end;
+
+begin
+  Result:='';
+  SetLength(Result,4*Length(S));
+  iIn:=1;
+  iOut:=0;
+  While iIn<=Length(S) do
+    begin
+    C:=S[iIn];
+    If C in [#0..' ','"'] then
+      begin
+      O:='\'+HexStr(Ord(C),2);
+      AddO;
+      end
+    else if Ord(C)<128 then
+      begin
+      inc(iOut);
+      Result[iOut]:=C;
+      end
+    else
+      begin
+      I:=U8length(C);
+      if (I>0) then
+        begin
+        U:=Copy(S,iIn,I);
+        W:=Utf8Decode(U);
+        for I:=1 to Length(W) do
+          begin
+          O:='\'+HexStr(Ord(W[I]),4);
+          AddO;
+          end;
+        end;
+      end;
+    Inc(iIn);
+    end;
+  SetLength(Result,iOut);
+  Result:='"'+Result+'"';
+end;
+
+{ TCSSListElement }
+
+function TCSSListElement.GetAsString(aFormat: Boolean; const aIndent: String): UTF8String;
+
+Var
+  I : integer;
+
+begin
+  Result:='';
+  For I:=0 to ChildCount-1 do
+    begin
+    if I>0 then
+      Result:=Result+' ';
+    Result:=Result+Children[I].GetAsString(aFormat,aIndent);
+    end;
+
+end;
+
+function TCSSListElement.ExtractElement(aIndex: Integer): TCSSElement;
+begin
+  Result:=Children[aIndex];
+  FChildren.OwnsObjects:=False;
+  try
+    FChildren.Delete(aIndex);
+  finally
+    FChildren.OwnsObjects:=True;
+  end;
+end;
+
+{ TCSSAtRuleElement }
+
+function TCSSAtRuleElement.GetAsString(aFormat: Boolean; const aIndent: String
+  ): UTF8String;
+begin
+  Result:=DoGetAsString(AtKeyWord+' ',aFormat, aIndent);
+end;
+
+{ TCSSBaseStringElement }
+
+function TCSSBaseStringElement.GetAsString(aFormat: Boolean;
+  const aIndent: String): UTF8String;
+begin
+  Result:=Value;
+  if aFormat then
+    Result:=aIndent+Result;
+end;
+
+{ TUnicodeRangeElement }
+
+class function TCSSUnicodeRangeElement.CSSType: TCSSType;
+begin
+  Result:=csstUNICODERANGE;
+end;
+
+{ TCSSURLElement }
+
+class function TCSSURLElement.CSSType: TCSSType;
+begin
+  Result:=csstURL;
+end;
+
+
+{ TCSSCompoundElement }
+
+function TCSSCompoundElement.GetAsString(aFormat: Boolean; const aIndent: String
+  ): UTF8String;
+
+Var
+  I : Integer;
+
+begin
+  Result:='';
+  For I:=0 to ChildCount-1 do
+    begin
+    if (i>0) and aFormat then
+      Result:=Result+sLineBreak+aIndent;
+    Result:=Result+Children[I].GetAsString(aFormat,aIndent);
+    end;
+  if aFormat then
+    Result:=aIndent+Result;
+end;
+
+class function TCSSCompoundElement.CSSType: TCSSType;
+begin
+  Result:=csstCompound;
+end;
+
+{ TCSSDeclarationElement }
+
+function TCSSDeclarationElement.GetKeyCount: Integer;
+begin
+  If Assigned(FKeys) then
+    Result:=FKeys.Count
+  else
+    Result:=0;
+end;
+
+function TCSSDeclarationElement.GetKeys(aIndex : Integer): TCSSElement;
+begin
+  if Not Assigned(FKeys) then
+    Raise EListError.CreateFmt(SListIndexError,[aIndex]);
+  Result:=FKeys[aIndex];
+end;
+
+function TCSSDeclarationElement.GetAsString(aFormat: Boolean; const aIndent: String): UTF8String;
+
+var
+  I : Integer;
+
+begin
+  Result:='';
+  For I:=0 to KeyCount-1 do
+    begin
+    if (I>0) then
+      begin
+      Result:=Result+','+sLineBreak;
+      if aFormat then
+        Result:=Result+aIndent;
+      end;
+    Result:=Result+Keys[I].GetAsString(aFormat,aIndent);
+    end;
+  Result:=Result+' : ';
+  For I:=0 to ChildCount-1 do
+    begin
+    if (I>0) then
+      Result:=Result+', ';
+    Result:=Result+Children[I].GetAsString(aFormat,aIndent);
+    end;
+  Result:=aIndent+Result;
+end;
+
+procedure TCSSDeclarationElement.IterateChildren(aVisitor: TCSSTreeVisitor);
+begin
+  if Assigned(FKeys) then
+    FKeys.Iterate(aVisitor);
+  inherited IterateChildren(aVisitor);
+end;
+
+class function TCSSDeclarationElement.CSSType: TCSSType;
+begin
+  Result:=csstDECLARATION;
+end;
+
+destructor TCSSDeclarationElement.Destroy;
+begin
+  FreeAndNil(FKeys);
+  inherited Destroy;
+end;
+
+procedure TCSSDeclarationElement.AddKey(aKey: TCSSElement);
+begin
+  if Not Assigned(FKeys) then
+    FKeys:=TCSSElementList.Create(True);
+  FKeys.Add(aKey);
+end;
+
+{ TCSSUnaryElement }
+
+class function TCSSUnaryElement.CSSType: TCSSType;
+begin
+  Result:=csstUNARYOP;
+end;
+
+function TCSSUnaryElement.GetAsString(aFormat: Boolean; const aIndent: String): UTF8String;
+
+begin
+  Result:=UnaryOperators[Self.Operation];
+  if Not (Operation in [uoDoubleColon]) then
+    Result:=Result+' ';
+  if Assigned(Right) then
+    Result:=Result+Right.GetAsString(aFormat,aIndent);
+  if aFormat then
+    Result:=aIndent+Result;
+end;
+
+{ TCSSRuleElement }
+
+function TCSSRuleElement.GetSelector(aIndex : Integer): TCSSElement;
+
+begin
+  if not assigned(FSelectors) then
+     Raise EListError.CreateFmt(SListIndexError,[aIndex]);
+  Result:=FSelectors.Elements[aIndex];
+end;
+
+
+function TCSSRuleElement.GetSelectorCount: Integer;
+
+begin
+  if Assigned(FSelectors) then
+    Result:=FSelectors.Count
+  else
+    Result:=0;
+end;
+
+
+function TCSSRuleElement.GetAsString(aFormat: Boolean; const aIndent: String
+  ): UTF8String;
+
+begin
+  Result:=DoGetAsString('',aFormat,aIndent);
+end;
+
+function TCSSRuleElement.DoGetAsString(const aPrefix : String; aFormat: Boolean; const aIndent: String
+  ): UTF8String;
+
+var
+  I : Integer;
+  lIndent : String;
+
+begin
+  Result:='';
+  For I:=0 to SelectorCount-1 do
+    begin
+    if (I>0) then
+      begin
+      Result:=Result+',';
+      if aFormat then
+        Result:=Result+sLineBreak+aIndent
+      else
+        Result:=Result+' ';
+      end;
+    Result:=Result+Selectors[I].GetAsString(aFormat,aIndent);
+    end;
+  if (ChildCount=0) and (aPrefix<>'') then
+    Result:=aIndent+aPrefix+Result+';'
+  else
+    begin
+    if SelectorCount>0 then
+      Result:=Result+' ';
+    Result:=Result+'{';
+    lIndent:=aIndent;
+    if aFormat then
+      begin
+      lIndent:=lIndent+sIndent;
+      Result:=Result+sLineBreak;
+      end
+    else
+      Result:=Result+' ';
+    For I:=0 to ChildCount-1 do
+      begin
+      if (I>0) then
+        begin
+        if aFormat then
+          Result:=Result+sLineBreak
+        else
+          Result:=Result+' ';
+        end;
+      Result:=Result+Children[I].GetAsString(aFormat,lIndent)+';';
+      end;
+    if aFormat then
+      Result:=Result+sLineBreak+aIndent
+    else
+      Result:=Result+' ';
+    Result:=Result+'}';
+    Result:=aPrefix+Result;
+    if aFormat then
+      Result:=aIndent+Result;
+    end;
+end;
+
+procedure TCSSRuleElement.IterateChildren(aVisitor: TCSSTreeVisitor);
+begin
+  if Assigned(FSelectors) then
+    FSelectors.Iterate(aVisitor);
+  inherited IterateChildren(aVisitor);
+end;
+
+class function TCSSRuleElement.CSSType: TCSSType;
+begin
+  Result:=csstRULE
+end;
+
+destructor TCSSRuleElement.Destroy;
+begin
+  FreeAndNil(FSelectors);
+  Inherited Destroy;
+end;
+
+procedure TCSSRuleElement.AddSelector(aSelector: TCSSElement);
+begin
+  if Not Assigned(aSelector) then
+      exit;
+  if not Assigned(FSelectors) then
+    FSelectors:=TCSSElementList.Create(True);
+  FSelectors.Add(aSelector);
+end;
+
+{ TCSSPseudoClassElement }
+
+procedure TCSSPseudoClassElement.SetElement(AValue: TCSSElement);
+begin
+  if FElement=AValue then Exit;
+  FreeAndNil(FElement);
+  FElement:=AValue;
+end;
+
+class function TCSSPseudoClassElement.CSSType: TCSSType;
+begin
+  Result:=csstPSEUDOCLASS;
+end;
+
+destructor TCSSPseudoClassElement.Destroy;
+begin
+  Element:=Nil;
+  inherited Destroy;
+end;
+
+{ TCSSChildrenElement }
+
+function TCSSChildrenElement.GetChild(aIndex : Integer): TCSSElement;
+begin
+  if not Assigned(FChildren) then
+    Raise EListError.CreateFmt(SListIndexError,[aIndex]);
+  Result:=FChildren[AIndex];
+end;
+
+function TCSSChildrenElement.GetChildCount: Integer;
+begin
+  if not Assigned(FChildren) then
+    Result:=0
+  else
+    Result:=FChildren.Count;
+end;
+
+procedure TCSSChildrenElement.IterateChildren(aVisitor: TCSSTreeVisitor);
+begin
+  inherited IterateChildren(aVisitor);
+  If Assigned(FChildren) then
+    FChildren.Iterate(aVisitor);
+end;
+
+destructor TCSSChildrenElement.Destroy;
+begin
+  FreeAndNil(FChildren);
+  inherited Destroy;
+end;
+
+
+procedure TCSSChildrenElement.AddChild(aChild: TCSSElement);
+begin
+  if Not Assigned(aChild) then
+    exit;
+  if FChildren=Nil then
+     FChildren:=TCSSElementList.Create;
+  FChildren.Add(aChild);
+end;
+
+
+{ TCSSCallElement }
+
+function TCSSCallElement.GetArg(aIndex : Integer): TCSSElement;
+begin
+  Result:=Children[AIndex];
+end;
+
+function TCSSCallElement.GetArgCount: Integer;
+begin
+  Result:=ChildCount;
+end;
+
+function TCSSCallElement.GetAsString(aFormat: Boolean; const aIndent: String): UTF8String;
+
+Var
+  I : Integer;
+
+begin
+  Result:=Name+'(';
+  For I:=0 to ChildCount-1 do
+    begin
+    if I>0 then
+      Result:=Result+', ';
+    Result:=Result+Children[I].GetAsString(aFormat,aIndent);
+    end;
+  if aFormat then
+    Result:=aIndent+Result;
+  Result:=Result+')'
+end;
+
+class function TCSSCallElement.CSSType: TCSSType;
+begin
+  Result:=csstCall;
+end;
+
+procedure TCSSCallElement.AddArg(aArg: TCSSElement);
+begin
+  AddChild(aArg);
+end;
+
+{ TCSSFloatElement }
+
+function TCSSFloatElement.GetAsString(aFormat : Boolean; const aIndent : String): UTF8String;
+begin
+  Str(Value:5:2,Result);
+  Result:=TrimLeft(Result); // Space for positive numbers
+  if aFormat then
+    Result:=aIndent+Result;
+end;
+
+class function TCSSFloatElement.CSSType: TCSSType;
+begin
+  Result:=csstFloat;
+end;
+
+{ TCSSStringElement }
+
+function TCSSStringElement.GetChildren: TCSSElementList;
+begin
+  if FChildren=Nil then
+     FChildren:=TCSSElementList.Create(True);
+  Result:=FChildren;
+end;
+
+function TCSSStringElement.GetAsString(aFormat : Boolean; const aIndent : String): UTF8String;
+begin
+  Result:=StringToCSSString(Value);
+  if aFormat then
+    Result:=aIndent+Result;
+end;
+
+procedure TCSSStringElement.IterateChildren(aVisitor: TCSSTreeVisitor);
+begin
+  inherited IterateChildren(aVisitor);
+  if Assigned(FChildren) then
+    FChildren.Iterate(aVisitor);
+end;
+
+class function TCSSStringElement.CSSType: TCSSType;
+begin
+  Result:=csstSTRING;
+end;
+
+destructor TCSSStringElement.Destroy;
+begin
+  FreeAndNil(FChildren);
+  inherited Destroy;
+end;
+
+{ TCSSClassNameElement }
+
+class function TCSSClassNameElement.CSSType: TCSSType;
+begin
+  Result:=csstCLASSNAME;
+end;
+
+{ TCSSIdentifierElement }
+
+function TCSSIdentifierElement.GetName: UTF8String;
+begin
+  Result:=Value;
+end;
+
+function TCSSIdentifierElement.GetAsString(aFormat : Boolean; const aIndent : String): UTF8String;
+begin
+  Result:=Value;
+end;
+
+class function TCSSIdentifierElement.CSSType: TCSSType;
+begin
+  Result:=csstIDENTIFIER;
+end;
+
+{ TCSSArrayElement }
+
+
+procedure TCSSArrayElement.SetPrefix(AValue: TCSSElement);
+begin
+  if FPrefix=AValue then Exit;
+  FreeAndNil(FPrefix);
+  FPrefix:=AValue;
+end;
+
+function TCSSArrayElement.GetAsString(aFormat: Boolean; const aIndent: String
+  ): UTF8String;
+var
+  I : integer;
+begin
+  Result:='[';
+  For I:=0 to ChildCount-1 do
+    begin
+    if I>0 then
+      Result:=Result+' ';
+    Result:=Result+Children[I].GetAsString(aFormat, aIndent);
+    end;
+  Result:=aIndent+Result+']';
+end;
+
+destructor TCSSArrayElement.Destroy;
+begin
+  Prefix:=Nil;
+  inherited Destroy;
+end;
+
+class function TCSSArrayElement.CSSType: TCSSType;
+begin
+  Result:=csstArray;
+end;
+
+
+{ TCSSElementList }
+
+function TCSSElementList.GetElement(aIndex : Integer): TCSSElement;
+begin
+  Result:=TCSSElement(Items[aIndex]);
+end;
+
+procedure TCSSElementList.Iterate(aVisitor: TCSSTreeVisitor);
+
+Var
+  I : Integer;
+
+begin
+  For I:=0 to Count-1 do
+    Elements[i].Iterate(aVisitor);
+end;
+
+{ TCSSIntegerElement }
+
+function TCSSIntegerElement.GetAsString(aFormat : Boolean; const aIndent : String): UTF8String;
+begin
+  Result:=IntToStr(Value);
+  if aFormat then
+    Result:=aIndent+Result;
+end;
+
+class function TCSSIntegerElement.CSSType: TCSSType;
+begin
+  Result:=csstINTEGER;
+end;
+
+{ TCSSBinaryElement }
+
+procedure TCSSBinaryElement.SetLeft(AValue: TCSSElement);
+begin
+  if FLeft=AValue then Exit;
+  FreeAndNil(FLeft);
+  FLeft:=AValue;
+end;
+
+function TCSSBinaryElement.GetAsString(aFormat: Boolean; const aIndent: String
+  ): UTF8String;
+begin
+  Result:='';
+  if Assigned(Left) then
+    Result:=Left.GetAsString(aFormat,aIndent);
+  if Not (Operation in [boColon,boDoubleColon]) then
+    Result:=Result+' '+BinaryOperators[Operation]+' '
+  else
+    Result:=Result+BinaryOperators[Operation];
+  if Assigned(Right) then
+    Result:=Result+Right.GetAsString(aFormat,aIndent);
+end;
+
+destructor TCSSBinaryElement.Destroy;
+begin
+  Left:=Nil;
+  inherited Destroy;
+end;
+
+class function TCSSBinaryElement.CSSType: TCSSType;
+begin
+  Result:=csstBINARYOP;
+end;
+
+procedure TCSSBinaryElement.IterateChildren(aVisitor: TCSSTreeVisitor);
+begin
+  inherited IterateChildren(aVisitor);
+  if Assigned(FLeft) then
+    FLeft.Iterate(aVisitor);
+
+end;
+
+{ TCSSUnaryElement }
+
+procedure TCSSBaseUnaryElement.SetRight(AValue: TCSSElement);
+begin
+  if FRight=AValue then Exit;
+  FreeAndNil(FRight);
+  FRight:=AValue;
+end;
+
+
+destructor TCSSBaseUnaryElement.Destroy;
+begin
+  Right:=Nil;
+  inherited Destroy;
+end;
+
+procedure TCSSBaseUnaryElement.IterateChildren(aVisitor: TCSSTreeVisitor);
+begin
+  inherited IterateChildren(aVisitor);
+  If Assigned(FRight) then
+     FRight.Iterate(aVisitor);
+end;
+
+{ TCSSElement }
+
+function TCSSElement.GetAsUnFormattedString: UTF8String;
+begin
+  Result:=GetAsString(False,'');
+end;
+
+function TCSSElement.GetAsFormattedString: UTF8String;
+begin
+  Result:=GetAsString(True,'');
+end;
+
+function TCSSElement.GetAsString(aFormat: Boolean; const aIndent : String): UTF8String;
+begin
+  Result:='';
+end;
+
+procedure TCSSElement.IterateChildren(aVisitor: TCSSTreeVisitor);
+begin
+  if Assigned(aVisitor) then ;
+end;
+
+constructor TCSSElement.Create(const aFileName: UTF8String; aRow, aCol: Integer);
+begin
+  FFileName:=aFileName;
+  FRow:=aRow;
+  FCol:=aCol;
+end;
+
+class function TCSSElement.CSSType: TCSSType;
+begin
+  Result:=csstUnknown;
+end;
+
+procedure TCSSElement.Iterate(aVisitor: TCSSTreeVisitor);
+begin
+  aVisitor.Visit(Self);
+  IterateChildren(aVisitor);
+end;
+
+end.
+

+ 196 - 0
packages/fcl-css/src/fpcssutils.pp

@@ -0,0 +1,196 @@
+{
+    This file is part of the Free Pascal Run time library.
+    Copyright (c) 2022- by Michael Van Canneyt ([email protected])
+
+    This file contains CSS utility class
+
+    See the File COPYING.FPC, included in this distribution,
+    for details about the copyright.
+
+    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.
+
+ **********************************************************************}
+
+unit fpcssutils;
+
+{$mode objfpc}{$H+}
+
+interface
+
+uses
+  TypInfo, Classes, SysUtils, types, fpcsstree, fpcssparser, fpcssscanner;
+
+Type
+
+  { TClassNameVisitor }
+
+  TClassNameVisitor = Class(TCSSTreeVisitor)
+  private
+    FList: TStrings;
+  public
+    Constructor Create(aList: TStrings);
+    Procedure Visit(obj: TCSSElement); override;
+    property List : TStrings Read FList;
+  end;
+
+  { TCSSUtils }
+
+  TCSSUtils = class(TComponent)
+    Procedure ExtractClassNames(Const aFileName : String; aList : TStrings);
+    Procedure ExtractClassNames(Const aStream : TStream; aList : TStrings);
+    Procedure ExtractClassNames(Const aElement : TCSSElement; aList : TStrings);
+    Function ExtractClassNames(Const aFileName : String) : TStringDynArray;
+    Function ExtractClassNames(Const aStream : TStream) : TStringDynArray;
+    Function ExtractClassNames(Const aElement : TCSSElement) : TStringDynArray;
+    Procedure Minimize(aInput,aOutput : TStream);
+  end;
+
+implementation
+
+{ TClassNameVisitor }
+
+constructor TClassNameVisitor.Create(aList: TStrings);
+begin
+  FList:=aList;
+end;
+
+procedure TClassNameVisitor.Visit(obj: TCSSElement);
+begin
+  if Obj.CSSType=csstCLASSNAME then
+    FList.Add(Obj.AsString);
+end;
+
+{ TCSSUtils }
+
+procedure TCSSUtils.ExtractClassNames(const aFileName: String; aList: TStrings);
+
+Var
+  S : TStringStream;
+
+begin
+  S:=TStringStream.Create;
+  try
+    S.LoadFromFile(aFileName);
+    ExtractClassNames(S,aList);
+  finally
+    S.Free;
+  end;
+end;
+
+function TCSSUtils.ExtractClassNames(const aFileName: String): TStringDynArray;
+
+Var
+  L : TStrings;
+
+begin
+  L:=TStringList.Create;
+  try
+    ExtractClassNames(aFileName,L);
+    Result:=L.ToStringArray;
+  finally
+    L.Free;
+  end;
+end;
+
+procedure TCSSUtils.ExtractClassNames(const aStream: TStream; aList: TStrings);
+
+Var
+  aParser : TCSSParser;
+  aElement : TCSSElement;
+
+begin
+  aElement:=Nil;
+  aParser:=TCSSParser.Create(aStream);
+  try
+    aElement:=aParser.Parse;
+    ExtractClassNames(aElement,aList);
+  finally
+    aElement.Free;
+    aParser.Free;
+  end;
+end;
+
+procedure TCSSUtils.ExtractClassNames(const aElement: TCSSElement; aList: TStrings);
+
+Var
+  aVis : TClassNameVisitor;
+
+begin
+  aVis:=TClassNameVisitor.Create(aList);
+  try
+    aElement.Iterate(aVis);
+  finally
+    aVis.Free;
+  end;
+end;
+
+function TCSSUtils.ExtractClassNames(const aStream: TStream): TStringDynArray;
+
+Var
+  L : TStrings;
+
+begin
+  L:=TStringList.Create;
+  try
+    ExtractClassNames(aStream,L);
+    Result:=L.ToStringArray;
+  finally
+    L.Free;
+  end;
+end;
+
+function TCSSUtils.ExtractClassNames(const aElement: TCSSElement): TStringDynArray;
+
+Var
+  L : TStrings;
+
+begin
+  L:=TStringList.Create;
+  try
+    ExtractClassNames(aElement,L);
+    Result:=L.ToStringArray;
+  finally
+    L.Free;
+  end;
+
+end;
+
+procedure TCSSUtils.Minimize(aInput, aOutput: TStream);
+
+Var
+  aScanner : TCSSScanner;
+  aToken,aPreviousToken : TCSSToken;
+  S : UTF8String;
+
+begin
+  aPrevioustoken:=ctkWHITESPACE;
+  AScanner:=TCSSScanner.Create(aInput);
+  try
+    aScanner.ReturnWhiteSpace:=True;
+    aToken:=aScanner.FetchToken;
+    While (aToken<>ctkEOF) do
+      begin
+      if aToken=ctkSTRING then
+        S:=StringToCSSString(aScanner.CurTokenString)
+      else if aToken<>ctkWHITESPACE then
+        S:=aScanner.CurTokenString
+      else if aPreviousToken<>ctkWHITESPACE then
+        S:=' '
+      else
+        S:='';
+      writeln(GetEnumName(TypeInfo(TCSSTOKEN),Ord(aToken)),' -> S : >',S,'<');
+      if S<>'' then
+        aOutput.WriteBuffer(S[1],length(S));
+      aPreviousToken:=aToken;
+      aToken:=aScanner.FetchToken;
+      end;
+
+  finally
+    aScanner.Free;
+  end;
+end;
+
+end.
+

+ 7 - 0
packages/fcl-css/tests/css/absolute.css

@@ -0,0 +1,7 @@
+
+div {
+    position: absolute;
+    left: -28px;
+    top: -4px;
+    bottom: 4;
+}

+ 10 - 0
packages/fcl-css/tests/css/animation.css

@@ -0,0 +1,10 @@
+
+@-webkit-keyframes dot {
+    0% {
+        transform: rotate(90deg);
+    }
+
+    100% {
+        transform: rotate(80deg);
+    }
+}

+ 3 - 0
packages/fcl-css/tests/css/anon.css

@@ -0,0 +1,3 @@
+background-color:red;
+color:blue;
+font-size:14px;

+ 14 - 0
packages/fcl-css/tests/css/bigbig.css

@@ -0,0 +1,14 @@
+@font-face {
+  font-family: 'Montserrat';
+  font-style: italic;
+  font-weight: 100;
+  font-display: swap;
+  src: url(https://fonts.gstatic.com/s/montserrat/v18/JTUOjIg1_i6t8kCHKm459WxZqi7j.ttf) format('truetype');
+}
+@font-face {
+  font-family: 'Montserrat';
+  font-style: italic;
+  font-weight: 200;
+  font-display: swap;
+  src: url(https://fonts.gstatic.com/s/montserrat/v18/JTUPjIg1_i6t8kCHKm459WxZBg_D-w.ttf) format('truetype');
+}

+ 21 - 0
packages/fcl-css/tests/css/class.css

@@ -0,0 +1,21 @@
+
+.rangeProgress {
+    background-color: blue;
+}
+
+.rangeProgress:hover {
+    color: red;
+}
+
+.rangeProgress::boot {
+    color: green;
+}
+
+input:enabled:read-write:-webkit-any(:focus,:hover)::-webkit-clear-button {
+    opacity: 1;
+    pointer-events: auto;
+}
+
+.box {
+    color: red;
+}

+ 3 - 0
packages/fcl-css/tests/css/color.css

@@ -0,0 +1,3 @@
+div {
+    background-color: #FF0000;
+}

+ 7 - 0
packages/fcl-css/tests/css/font-face.css

@@ -0,0 +1,7 @@
+@font-face {
+  font-family: 'Montserrat';
+  font-style: italic;
+  font-weight: 200;
+  font-display: swap;
+  src: url(https://fonts.gstatic.com/s/montserrat/v18/JTUPjIg1_i6t8kCHKm459WxZBg_D-w.ttf) format('truetype');
+}

+ 24 - 0
packages/fcl-css/tests/css/font-face2.css

@@ -0,0 +1,24 @@
+/* cyrillic */
+@font-face {
+  font-family: 'Product Sans';
+  font-style: normal;
+  font-weight: 400;
+  src: url(https://fonts.gstatic.com/s/productsans/v18/pxiDypQkot1TnFhsFMOfGShVE9eOcEg.woff2) format('woff2');
+  unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
+}
+/* greek */
+@font-face {
+  font-family: 'Product Sans';
+  font-style: normal;
+  font-weight: 400;
+  src: url(https://fonts.gstatic.com/s/productsans/v18/pxiDypQkot1TnFhsFMOfGShVFNeOcEg.woff2) format('woff2');
+  unicode-range: U+0370-03FF;
+}
+/* latin-ext */
+@font-face {
+  font-family: 'Product Sans';
+  font-style: normal;
+  font-weight: 400;
+  src: url(https://fonts.gstatic.com/s/productsans/v18/pxiDypQkot1TnFhsFMOfGShVGdeOcEg.woff2) format('woff2');
+  unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
+}

+ 8 - 0
packages/fcl-css/tests/css/font.css

@@ -0,0 +1,8 @@
+
+body {
+    background-color: #f0f0f2;
+    margin: 0;
+    padding: 0;
+    font-family: -apple-system, system-ui, BlinkMacSystemFont, "Segoe UI", "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
+    color: rgba(255, 255, 255, 0.5);
+}

+ 49 - 0
packages/fcl-css/tests/css/hello.css

@@ -0,0 +1,49 @@
+input {
+  display: input;
+  padding: 2;
+  background-color: #FFFFFF;
+  width: 177px;
+  height: 32px;
+  color: #000000;
+  border-top-color: rgb(48,48,48);
+  border-left-color: rgb(48,48,48);
+  border-right-color: rgb(48,48,48);
+  border-bottom-color: rgb(48,48,48);
+  border-width: 1px;
+  font-family: "./assets/font/KaTeX_SansSerif-Regular.ttf";
+  font-size: 14px;
+}
+
+
+button {
+  background-color: rgb(239, 239, 239);
+  width: 53px;
+  height: 21px;
+  color: #000000;
+  border-width: 1px;
+  border-top-color: rgb(48,48,48);
+  border-left-color: rgb(48,48,48);
+  border-right-color: rgb(48,48,48);
+  border-bottom-color: rgb(48,48,48);
+  font-family: "./assets/font/KaTeX_SansSerif-Regular.ttf";
+  font-size: 14px;
+  text-align: center;
+}
+
+
+textarea {
+    padding: 2;
+    text-rendering: auto;
+    color: #000000;
+    background-color: rgb(250, 250, 250);
+    border-width: 1px;
+    border-style: solid;
+    border-color: rgb(133, 133, 133);
+    height: 34px;
+    width: 179px;
+}
+
+textarea:focus {
+  border-color: #FF0000;
+  border-width: 2px;
+}

+ 3 - 0
packages/fcl-css/tests/css/id.css

@@ -0,0 +1,3 @@
+#main {
+    display: block;
+}

+ 8 - 0
packages/fcl-css/tests/css/input_type.css

@@ -0,0 +1,8 @@
+
+input[type="text"] {
+    color: black;
+}
+
+input[type="range"] {
+    color: red;
+}

+ 8 - 0
packages/fcl-css/tests/css/margin.css

@@ -0,0 +1,8 @@
+
+div {
+  margin: 25px 50px 75px 100px;
+}
+
+span {
+  margin: 8px;
+}

+ 7 - 0
packages/fcl-css/tests/css/media_query.css

@@ -0,0 +1,7 @@
+
+@media {
+  #main {
+    width: 100%;
+    min-width: 0;
+  }
+}

+ 1193 - 0
packages/fcl-css/tests/css/mystyle.css

@@ -0,0 +1,1193 @@
+#facebox .b {
+  background: url(__SYS__/jquery/facebox/b.png);
+}
+#facebox .tl {
+  background: url(__SYS__/jquery/facebox/tl.png);
+}
+#facebox .tr {
+  background: url(__SYS__/jquery/facebox/tr.png);
+}
+#facebox .bl {
+  background: url(__SYS__/jquery/facebox/bl.png);
+}
+#facebox .br {
+  background: url(__SYS__/jquery/facebox/br.png);
+}
+#facebox {
+  position: absolute;
+  width: 100%;
+  top: 0;
+  left: 0;
+  z-index: 1000;
+  text-align: left;
+}
+#facebox .popup {
+  position: relative;
+}
+#facebox table {
+  margin: auto;
+  border-collapse: collapse;
+}
+#facebox .body {
+  padding: 10px;
+  background: #fff;
+  width: 640px;
+}
+#facebox .loading {
+  text-align: center;
+}
+#facebox .image {
+  text-align: center;
+}
+#facebox img {
+  border: 0;
+}
+#facebox .footer {
+  border-top: 1px solid #dddddd;
+  padding-top: 5px;
+  margin-top: 10px;
+  text-align: right;
+}
+#facebox .tl,
+#facebox .tr,
+#facebox .bl,
+#facebox .br {
+  height: 10px;
+  width: 10px;
+  overflow: hidden;
+  padding: 0;
+}
+#jquery-overlay {
+  position: absolute;
+  top: 0;
+  left: 0;
+  z-index: 999;
+  width: 100%;
+  height: 500px;
+}
+#jquery-lightbox {
+  position: absolute;
+  top: 0;
+  left: 0;
+  width: 100%;
+  z-index: 1000;
+  text-align: center;
+  line-height: 0;
+}
+#jquery-lightbox a img {
+  border: none;
+}
+#lightbox-container-image-box {
+  position: relative;
+  background-color: #fff;
+  width: 250px;
+  height: 250px;
+  margin: 0 auto;
+}
+#lightbox-container-image {
+  padding: 10px;
+}
+#lightbox-loading {
+  position: absolute;
+  top: 40%;
+  left: 0%;
+  height: 25%;
+  width: 100%;
+  text-align: center;
+  line-height: 0;
+}
+#lightbox-nav {
+  position: absolute;
+  top: 0;
+  left: 0;
+  height: 100%;
+  width: 100%;
+  z-index: 998;
+}
+#lightbox-container-image-box > #lightbox-nav {
+  left: 0;
+}
+#lightbox-nav a {
+  outline: none;
+}
+#lightbox-nav-btnPrev,
+#lightbox-nav-btnNext {
+  width: 49%;
+  height: 100%;
+  zoom: 1;
+  display: block;
+}
+#lightbox-nav-btnPrev {
+  left: 0;
+  float: left;
+}
+#lightbox-nav-btnNext {
+  right: 0;
+  float: right;
+}
+#lightbox-container-image-data-box {
+  font: 10px Verdana, Helvetica, sans-serif;
+  background-color: #fff;
+  margin: 0 auto;
+  line-height: 1.4em;
+  overflow: auto;
+  width: 100%;
+  padding: 0 10px 0;
+}
+#lightbox-container-image-data {
+  padding: 0 10px;
+  color: #666;
+}
+#lightbox-container-image-data #lightbox-image-details {
+  width: 70%;
+  float: left;
+  text-align: left;
+}
+#lightbox-image-details-caption {
+  font-weight: bold;
+}
+#lightbox-image-details-currentNumber {
+  display: block;
+  clear: left;
+  padding-bottom: 1em;
+}
+#lightbox-secNav-btnClose {
+  width: 66px;
+  float: right;
+  padding-bottom: 0.7em;
+}
+.ui-helper-hidden {
+  display: none;
+}
+.ui-helper-hidden-accessible {
+  position: absolute;
+  left: -99999999px;
+}
+.ui-helper-reset {
+  margin: 0;
+  padding: 0;
+  border: 0;
+  outline: 0;
+  line-height: 1.3;
+  text-decoration: none;
+  font-size: 100%;
+  list-style: none;
+}
+.ui-helper-clearfix:after {
+  content: ".";
+  display: block;
+  height: 0;
+  clear: both;
+  visibility: hidden;
+}
+.ui-helper-clearfix {
+  display: inline-block;
+}
+* html .ui-helper-clearfix {
+  height: 1%;
+}
+.ui-helper-clearfix {
+  display: block;
+}
+.ui-helper-zfix {
+  width: 100%;
+  height: 100%;
+  top: 0;
+  left: 0;
+  position: absolute;
+  opacity: 0;
+  filter: Alpha(Opacity=0);
+}
+.ui-state-disabled {
+  cursor: default !important;
+}
+.ui-icon {
+  display: block;
+  text-indent: -99999px;
+  overflow: hidden;
+  background-repeat: no-repeat;
+}
+.ui-widget-overlay {
+  position: absolute;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+}
+.ui-widget {
+  font-family: Trebuchet MS, Tahoma, Verdana, Arial, sans-serif;
+  font-size: 1.1em;
+}
+.ui-widget input,
+.ui-widget select,
+.ui-widget textarea,
+.ui-widget button {
+  font-family: Trebuchet MS, Tahoma, Verdana, Arial, sans-serif;
+  font-size: 1em;
+}
+.ui-widget-content {
+  border: 1px solid #dddddd;
+  background: #eee url(images/ui-bg_highlight-soft_100_eeeeee_1x100.png) 50% top
+    repeat-x;
+  color: #333333;
+}
+.ui-widget-content a {
+  color: #333333;
+}
+.ui-widget-header {
+  border: 1px solid #e78f08;
+  background: #f6a828 url(images/ui-bg_gloss-wave_35_f6a828_500x100.png) 50% 50%
+    repeat-x;
+  color: #ffffff;
+  font-weight: bold;
+}
+.ui-widget-header a {
+  color: #ffffff;
+}
+.ui-state-default,
+.ui-widget-content .ui-state-default {
+  border: 1px solid #cccccc;
+  background: #f6f6f6 url(images/ui-bg_glass_100_f6f6f6_1x400.png) 50% 50%
+    repeat-x;
+  font-weight: bold;
+  color: #1c94c4;
+  outline: none;
+}
+.ui-state-default a,
+.ui-state-default a:link,
+.ui-state-default a:visited {
+  color: #1c94c4;
+  text-decoration: none;
+  outline: none;
+}
+.ui-state-hover,
+.ui-widget-content .ui-state-hover,
+.ui-state-focus,
+.ui-widget-content .ui-state-focus {
+  border: 1px solid #fbcb09;
+  background: #fdf5ce url(images/ui-bg_glass_100_fdf5ce_1x400.png) 50% 50%
+    repeat-x;
+  font-weight: bold;
+  color: #c77405;
+  outline: none;
+}
+.ui-state-hover a,
+.ui-state-hover a:hover {
+  color: #c77405;
+  text-decoration: none;
+  outline: none;
+}
+.ui-state-active,
+.ui-widget-content .ui-state-active {
+  border: 1px solid #fbd850;
+  background: #fff url(images/ui-bg_glass_65_ffffff_1x400.png) 50% 50% repeat-x;
+  font-weight: bold;
+  color: #eb8f00;
+  outline: none;
+}
+.ui-state-active a,
+.ui-state-active a:link,
+.ui-state-active a:visited {
+  color: #eb8f00;
+  outline: none;
+  text-decoration: none;
+}
+.ui-state-highlight,
+.ui-widget-content .ui-state-highlight {
+  border: 1px solid #fed22f;
+  background: #ffe45c url(images/ui-bg_highlight-soft_75_ffe45c_1x100.png) 50%
+    top repeat-x;
+  color: #363636;
+}
+.ui-state-highlight a,
+.ui-widget-content .ui-state-highlight a {
+  color: #363636;
+}
+.ui-state-error,
+.ui-widget-content .ui-state-error {
+  border: 1px solid #cd0a0a;
+  background: #b81900 url(images/ui-bg_diagonals-thick_18_b81900_40x40.png) 50%
+    50% repeat;
+  color: #ffffff;
+}
+.ui-state-error a,
+.ui-widget-content .ui-state-error a {
+  color: #ffffff;
+}
+.ui-state-error-text,
+.ui-widget-content .ui-state-error-text {
+  color: #ffffff;
+}
+.ui-state-disabled,
+.ui-widget-content .ui-state-disabled {
+  opacity: 0.35;
+  filter: Alpha(Opacity=35);
+  background-image: none;
+}
+.ui-priority-primary,
+.ui-widget-content .ui-priority-primary {
+  font-weight: bold;
+}
+.ui-priority-secondary,
+.ui-widget-content .ui-priority-secondary {
+  opacity: 0.7;
+  filter: Alpha(Opacity=70);
+  font-weight: normal;
+}
+.ui-icon {
+  width: 16px;
+  height: 16px;
+  background-image: url(images/ui-icons_222222_256x240.png);
+}
+.ui-widget-content .ui-icon {
+  background-image: url(images/ui-icons_222222_256x240.png);
+}
+.ui-widget-header .ui-icon {
+  background-image: url(images/ui-icons_ffffff_256x240.png);
+}
+.ui-state-default .ui-icon {
+  background-image: url(images/ui-icons_ef8c08_256x240.png);
+}
+.ui-state-hover .ui-icon,
+.ui-state-focus .ui-icon {
+  background-image: url(images/ui-icons_ef8c08_256x240.png);
+}
+.ui-state-active .ui-icon {
+  background-image: url(images/ui-icons_ef8c08_256x240.png);
+}
+.ui-state-highlight .ui-icon {
+  background-image: url(images/ui-icons_228ef1_256x240.png);
+}
+.ui-state-error .ui-icon,
+.ui-state-error-text .ui-icon {
+  background-image: url(images/ui-icons_ffd27a_256x240.png);
+}
+.ui-icon-carat-1-n {
+  background-position: 0 0;
+}
+.ui-icon-carat-1-ne {
+  background-position: -16px 0;
+}
+.ui-icon-carat-1-e {
+  background-position: -32px 0;
+}
+.ui-icon-carat-1-se {
+  background-position: -48px 0;
+}
+.ui-icon-carat-1-s {
+  background-position: -64px 0;
+}
+.ui-icon-carat-1-sw {
+  background-position: -80px 0;
+}
+.ui-icon-carat-1-w {
+  background-position: -96px 0;
+}
+.ui-icon-carat-1-nw {
+  background-position: -112px 0;
+}
+.ui-icon-carat-2-n-s {
+  background-position: -128px 0;
+}
+.ui-icon-carat-2-e-w {
+  background-position: -144px 0;
+}
+.ui-icon-triangle-1-n {
+  background-position: 0 -16px;
+}
+.ui-icon-triangle-1-ne {
+  background-position: -16px -16px;
+}
+.ui-icon-triangle-1-e {
+  background-position: -32px -16px;
+}
+.ui-icon-triangle-1-se {
+  background-position: -48px -16px;
+}
+.ui-icon-triangle-1-s {
+  background-position: -64px -16px;
+}
+.ui-icon-triangle-1-sw {
+  background-position: -80px -16px;
+}
+.ui-icon-triangle-1-w {
+  background-position: -96px -16px;
+}
+.ui-icon-triangle-1-nw {
+  background-position: -112px -16px;
+}
+.ui-icon-triangle-2-n-s {
+  background-position: -128px -16px;
+}
+.ui-icon-triangle-2-e-w {
+  background-position: -144px -16px;
+}
+.ui-icon-arrow-1-n {
+  background-position: 0 -32px;
+}
+.ui-icon-arrow-1-ne {
+  background-position: -16px -32px;
+}
+.ui-icon-arrow-1-e {
+  background-position: -32px -32px;
+}
+.ui-icon-arrow-1-se {
+  background-position: -48px -32px;
+}
+.ui-icon-arrow-1-s {
+  background-position: -64px -32px;
+}
+.ui-icon-arrow-1-sw {
+  background-position: -80px -32px;
+}
+.ui-icon-arrow-1-w {
+  background-position: -96px -32px;
+}
+.ui-icon-arrow-1-nw {
+  background-position: -112px -32px;
+}
+.ui-icon-arrow-2-n-s {
+  background-position: -128px -32px;
+}
+.ui-icon-arrow-2-ne-sw {
+  background-position: -144px -32px;
+}
+.ui-icon-arrow-2-e-w {
+  background-position: -160px -32px;
+}
+.ui-icon-arrow-2-se-nw {
+  background-position: -176px -32px;
+}
+.ui-icon-arrowstop-1-n {
+  background-position: -192px -32px;
+}
+.ui-icon-arrowstop-1-e {
+  background-position: -208px -32px;
+}
+.ui-icon-arrowstop-1-s {
+  background-position: -224px -32px;
+}
+.ui-icon-arrowstop-1-w {
+  background-position: -240px -32px;
+}
+.ui-icon-arrowthick-1-n {
+  background-position: 0 -48px;
+}
+.ui-icon-arrowthick-1-ne {
+  background-position: -16px -48px;
+}
+.ui-icon-arrowthick-1-e {
+  background-position: -32px -48px;
+}
+.ui-icon-arrowthick-1-se {
+  background-position: -48px -48px;
+}
+.ui-icon-arrowthick-1-s {
+  background-position: -64px -48px;
+}
+.ui-icon-arrowthick-1-sw {
+  background-position: -80px -48px;
+}
+.ui-icon-arrowthick-1-w {
+  background-position: -96px -48px;
+}
+.ui-icon-arrowthick-1-nw {
+  background-position: -112px -48px;
+}
+.ui-icon-arrowthick-2-n-s {
+  background-position: -128px -48px;
+}
+.ui-icon-arrowthick-2-ne-sw {
+  background-position: -144px -48px;
+}
+.ui-icon-arrowthick-2-e-w {
+  background-position: -160px -48px;
+}
+.ui-icon-arrowthick-2-se-nw {
+  background-position: -176px -48px;
+}
+.ui-icon-arrowthickstop-1-n {
+  background-position: -192px -48px;
+}
+.ui-icon-arrowthickstop-1-e {
+  background-position: -208px -48px;
+}
+.ui-icon-arrowthickstop-1-s {
+  background-position: -224px -48px;
+}
+.ui-icon-arrowthickstop-1-w {
+  background-position: -240px -48px;
+}
+.ui-icon-arrowreturnthick-1-w {
+  background-position: 0 -64px;
+}
+.ui-icon-arrowreturnthick-1-n {
+  background-position: -16px -64px;
+}
+.ui-icon-arrowreturnthick-1-e {
+  background-position: -32px -64px;
+}
+.ui-icon-arrowreturnthick-1-s {
+  background-position: -48px -64px;
+}
+.ui-icon-arrowreturn-1-w {
+  background-position: -64px -64px;
+}
+.ui-icon-arrowreturn-1-n {
+  background-position: -80px -64px;
+}
+.ui-icon-arrowreturn-1-e {
+  background-position: -96px -64px;
+}
+.ui-icon-arrowreturn-1-s {
+  background-position: -112px -64px;
+}
+.ui-icon-arrowrefresh-1-w {
+  background-position: -128px -64px;
+}
+.ui-icon-arrowrefresh-1-n {
+  background-position: -144px -64px;
+}
+.ui-icon-arrowrefresh-1-e {
+  background-position: -160px -64px;
+}
+.ui-icon-arrowrefresh-1-s {
+  background-position: -176px -64px;
+}
+.ui-icon-arrow-4 {
+  background-position: 0 -80px;
+}
+.ui-icon-arrow-4-diag {
+  background-position: -16px -80px;
+}
+.ui-icon-extlink {
+  background-position: -32px -80px;
+}
+.ui-icon-newwin {
+  background-position: -48px -80px;
+}
+.ui-icon-refresh {
+  background-position: -64px -80px;
+}
+.ui-icon-shuffle {
+  background-position: -80px -80px;
+}
+.ui-icon-transfer-e-w {
+  background-position: -96px -80px;
+}
+.ui-icon-transferthick-e-w {
+  background-position: -112px -80px;
+}
+.ui-icon-folder-collapsed {
+  background-position: 0 -96px;
+}
+.ui-icon-folder-open {
+  background-position: -16px -96px;
+}
+.ui-icon-document {
+  background-position: -32px -96px;
+}
+.ui-icon-document-b {
+  background-position: -48px -96px;
+}
+.ui-icon-note {
+  background-position: -64px -96px;
+}
+.ui-icon-mail-closed {
+  background-position: -80px -96px;
+}
+.ui-icon-mail-open {
+  background-position: -96px -96px;
+}
+.ui-icon-suitcase {
+  background-position: -112px -96px;
+}
+.ui-icon-comment {
+  background-position: -128px -96px;
+}
+.ui-icon-person {
+  background-position: -144px -96px;
+}
+.ui-icon-print {
+  background-position: -160px -96px;
+}
+.ui-icon-trash {
+  background-position: -176px -96px;
+}
+.ui-icon-locked {
+  background-position: -192px -96px;
+}
+.ui-icon-unlocked {
+  background-position: -208px -96px;
+}
+.ui-icon-bookmark {
+  background-position: -224px -96px;
+}
+.ui-icon-tag {
+  background-position: -240px -96px;
+}
+.ui-icon-home {
+  background-position: 0 -112px;
+}
+.ui-icon-flag {
+  background-position: -16px -112px;
+}
+.ui-icon-calendar {
+  background-position: -32px -112px;
+}
+.ui-icon-cart {
+  background-position: -48px -112px;
+}
+.ui-icon-pencil {
+  background-position: -64px -112px;
+}
+.ui-icon-clock {
+  background-position: -80px -112px;
+}
+.ui-icon-disk {
+  background-position: -96px -112px;
+}
+.ui-icon-calculator {
+  background-position: -112px -112px;
+}
+.ui-icon-zoomin {
+  background-position: -128px -112px;
+}
+.ui-icon-zoomout {
+  background-position: -144px -112px;
+}
+.ui-icon-search {
+  background-position: -160px -112px;
+}
+.ui-icon-wrench {
+  background-position: -176px -112px;
+}
+.ui-icon-gear {
+  background-position: -192px -112px;
+}
+.ui-icon-heart {
+  background-position: -208px -112px;
+}
+.ui-icon-star {
+  background-position: -224px -112px;
+}
+.ui-icon-link {
+  background-position: -240px -112px;
+}
+.ui-icon-cancel {
+  background-position: 0 -128px;
+}
+.ui-icon-plus {
+  background-position: -16px -128px;
+}
+.ui-icon-plusthick {
+  background-position: -32px -128px;
+}
+.ui-icon-minus {
+  background-position: -48px -128px;
+}
+.ui-icon-minusthick {
+  background-position: -64px -128px;
+}
+.ui-icon-close {
+  background-position: -80px -128px;
+}
+.ui-icon-closethick {
+  background-position: -96px -128px;
+}
+.ui-icon-key {
+  background-position: -112px -128px;
+}
+.ui-icon-lightbulb {
+  background-position: -128px -128px;
+}
+.ui-icon-scissors {
+  background-position: -144px -128px;
+}
+.ui-icon-clipboard {
+  background-position: -160px -128px;
+}
+.ui-icon-copy {
+  background-position: -176px -128px;
+}
+.ui-icon-contact {
+  background-position: -192px -128px;
+}
+.ui-icon-image {
+  background-position: -208px -128px;
+}
+.ui-icon-video {
+  background-position: -224px -128px;
+}
+.ui-icon-script {
+  background-position: -240px -128px;
+}
+.ui-icon-alert {
+  background-position: 0 -144px;
+}
+.ui-icon-info {
+  background-position: -16px -144px;
+}
+.ui-icon-notice {
+  background-position: -32px -144px;
+}
+.ui-icon-help {
+  background-position: -48px -144px;
+}
+.ui-icon-check {
+  background-position: -64px -144px;
+}
+.ui-icon-bullet {
+  background-position: -80px -144px;
+}
+.ui-icon-radio-off {
+  background-position: -96px -144px;
+}
+.ui-icon-radio-on {
+  background-position: -112px -144px;
+}
+.ui-icon-pin-w {
+  background-position: -128px -144px;
+}
+.ui-icon-pin-s {
+  background-position: -144px -144px;
+}
+.ui-icon-play {
+  background-position: 0 -160px;
+}
+.ui-icon-pause {
+  background-position: -16px -160px;
+}
+.ui-icon-seek-next {
+  background-position: -32px -160px;
+}
+.ui-icon-seek-prev {
+  background-position: -48px -160px;
+}
+.ui-icon-seek-end {
+  background-position: -64px -160px;
+}
+.ui-icon-seek-first {
+  background-position: -80px -160px;
+}
+.ui-icon-stop {
+  background-position: -96px -160px;
+}
+.ui-icon-eject {
+  background-position: -112px -160px;
+}
+.ui-icon-volume-off {
+  background-position: -128px -160px;
+}
+.ui-icon-volume-on {
+  background-position: -144px -160px;
+}
+.ui-icon-power {
+  background-position: 0 -176px;
+}
+.ui-icon-signal-diag {
+  background-position: -16px -176px;
+}
+.ui-icon-signal {
+  background-position: -32px -176px;
+}
+.ui-icon-battery-0 {
+  background-position: -48px -176px;
+}
+.ui-icon-battery-1 {
+  background-position: -64px -176px;
+}
+.ui-icon-battery-2 {
+  background-position: -80px -176px;
+}
+.ui-icon-battery-3 {
+  background-position: -96px -176px;
+}
+.ui-icon-circle-plus {
+  background-position: 0 -192px;
+}
+.ui-icon-circle-minus {
+  background-position: -16px -192px;
+}
+.ui-icon-circle-close {
+  background-position: -32px -192px;
+}
+.ui-icon-circle-triangle-e {
+  background-position: -48px -192px;
+}
+.ui-icon-circle-triangle-s {
+  background-position: -64px -192px;
+}
+.ui-icon-circle-triangle-w {
+  background-position: -80px -192px;
+}
+.ui-icon-circle-triangle-n {
+  background-position: -96px -192px;
+}
+.ui-icon-circle-arrow-e {
+  background-position: -112px -192px;
+}
+.ui-icon-circle-arrow-s {
+  background-position: -128px -192px;
+}
+.ui-icon-circle-arrow-w {
+  background-position: -144px -192px;
+}
+.ui-icon-circle-arrow-n {
+  background-position: -160px -192px;
+}
+.ui-icon-circle-zoomin {
+  background-position: -176px -192px;
+}
+.ui-icon-circle-zoomout {
+  background-position: -192px -192px;
+}
+.ui-icon-circle-check {
+  background-position: -208px -192px;
+}
+.ui-icon-circlesmall-plus {
+  background-position: 0 -208px;
+}
+.ui-icon-circlesmall-minus {
+  background-position: -16px -208px;
+}
+.ui-icon-circlesmall-close {
+  background-position: -32px -208px;
+}
+.ui-icon-squaresmall-plus {
+  background-position: -48px -208px;
+}
+.ui-icon-squaresmall-minus {
+  background-position: -64px -208px;
+}
+.ui-icon-squaresmall-close {
+  background-position: -80px -208px;
+}
+.ui-icon-grip-dotted-vertical {
+  background-position: 0 -224px;
+}
+.ui-icon-grip-dotted-horizontal {
+  background-position: -16px -224px;
+}
+.ui-icon-grip-solid-vertical {
+  background-position: -32px -224px;
+}
+.ui-icon-grip-solid-horizontal {
+  background-position: -48px -224px;
+}
+.ui-icon-gripsmall-diagonal-se {
+  background-position: -64px -224px;
+}
+.ui-icon-grip-diagonal-se {
+  background-position: -80px -224px;
+}
+.ui-corner-tl {
+  -moz-border-radius-topleft: 4px;
+  -webkit-border-top-left-radius: 4px;
+}
+.ui-corner-tr {
+  -moz-border-radius-topright: 4px;
+  -webkit-border-top-right-radius: 4px;
+}
+.ui-corner-bl {
+  -moz-border-radius-bottomleft: 4px;
+  -webkit-border-bottom-left-radius: 4px;
+}
+.ui-corner-br {
+  -moz-border-radius-bottomright: 4px;
+  -webkit-border-bottom-right-radius: 4px;
+}
+.ui-corner-top {
+  -moz-border-radius-topleft: 4px;
+  -webkit-border-top-left-radius: 4px;
+  -moz-border-radius-topright: 4px;
+  -webkit-border-top-right-radius: 4px;
+}
+.ui-corner-bottom {
+  -moz-border-radius-bottomleft: 4px;
+  -webkit-border-bottom-left-radius: 4px;
+  -moz-border-radius-bottomright: 4px;
+  -webkit-border-bottom-right-radius: 4px;
+}
+.ui-corner-right {
+  -moz-border-radius-topright: 4px;
+  -webkit-border-top-right-radius: 4px;
+  -moz-border-radius-bottomright: 4px;
+  -webkit-border-bottom-right-radius: 4px;
+}
+.ui-corner-left {
+  -moz-border-radius-topleft: 4px;
+  -webkit-border-top-left-radius: 4px;
+  -moz-border-radius-bottomleft: 4px;
+  -webkit-border-bottom-left-radius: 4px;
+}
+.ui-corner-all {
+  -moz-border-radius: 4px;
+  -webkit-border-radius: 4px;
+}
+.ui-widget-overlay {
+  background: #666 url(images/ui-bg_diagonals-thick_20_666666_40x40.png) 50% 50%
+    repeat;
+  opacity: 0.5;
+  filter: Alpha(Opacity=50);
+}
+.ui-widget-shadow {
+  margin: -5px 0 0 -5px;
+  padding: 5px;
+  background: #000 url(images/ui-bg_flat_10_000000_40x100.png) 50% 50% repeat-x;
+  opacity: 0.2;
+  filter: Alpha(Opacity=20);
+  -moz-border-radius: 5px;
+  -webkit-border-radius: 5px;
+}
+.ui-resizable {
+  position: relative;
+}
+.ui-resizable-handle {
+  position: absolute;
+  font-size: 0.1px;
+  z-index: 99999;
+  display: block;
+}
+.ui-resizable-disabled .ui-resizable-handle,
+.ui-resizable-autohide .ui-resizable-handle {
+  display: none;
+}
+.ui-resizable-n {
+  cursor: n-resize;
+  height: 7px;
+  width: 100%;
+  top: -5px;
+  left: 0px;
+}
+.ui-resizable-s {
+  cursor: s-resize;
+  height: 7px;
+  width: 100%;
+  bottom: -5px;
+  left: 0px;
+}
+.ui-resizable-e {
+  cursor: e-resize;
+  width: 7px;
+  right: -5px;
+  top: 0px;
+  height: 100%;
+}
+.ui-resizable-w {
+  cursor: w-resize;
+  width: 7px;
+  left: -5px;
+  top: 0px;
+  height: 100%;
+}
+.ui-resizable-se {
+  cursor: se-resize;
+  width: 12px;
+  height: 12px;
+  right: 1px;
+  bottom: 1px;
+}
+.ui-resizable-sw {
+  cursor: sw-resize;
+  width: 9px;
+  height: 9px;
+  left: -5px;
+  bottom: -5px;
+}
+.ui-resizable-nw {
+  cursor: nw-resize;
+  width: 9px;
+  height: 9px;
+  left: -5px;
+  top: -5px;
+}
+.ui-resizable-ne {
+  cursor: ne-resize;
+  width: 9px;
+  height: 9px;
+  right: -5px;
+  top: -5px;
+}
+.ui-accordion .ui-accordion-header {
+  cursor: pointer;
+  position: relative;
+  margin-top: 1px;
+  zoom: 1;
+}
+.ui-accordion .ui-accordion-li-fix {
+  display: inline;
+}
+.ui-accordion .ui-accordion-header-active {
+  border-bottom: 0 !important;
+}
+.ui-accordion .ui-accordion-header a {
+  display: block;
+  font-size: 1em;
+  padding: 0.5em 0.5em 0.5em 2.2em;
+}
+.ui-accordion .ui-accordion-header .ui-icon {
+  position: absolute;
+  left: 0.5em;
+  top: 50%;
+  margin-top: -8px;
+}
+.ui-accordion .ui-accordion-content {
+  padding: 1em 2.2em;
+  border-top: 0;
+  margin-top: -2px;
+  position: relative;
+  top: 1px;
+  margin-bottom: 2px;
+  overflow: auto;
+  display: none;
+}
+.ui-accordion .ui-accordion-content-active {
+  display: block;
+}
+.ui-dialog {
+  position: relative;
+  padding: 0.2em;
+  width: 300px;
+}
+.ui-dialog .ui-dialog-titlebar {
+  padding: 0.5em 0.3em 0.3em 1em;
+  position: relative;
+}
+.ui-dialog .ui-dialog-title {
+  float: left;
+  margin: 0.1em 0 0.2em;
+}
+.ui-dialog .ui-dialog-titlebar-close {
+  position: absolute;
+  right: 0.3em;
+  top: 50%;
+  width: 19px;
+  margin: -10px 0 0 0;
+  padding: 1px;
+  height: 18px;
+}
+.ui-dialog .ui-dialog-titlebar-close span {
+  display: block;
+  margin: 1px;
+}
+.ui-dialog .ui-dialog-titlebar-close:hover,
+.ui-dialog .ui-dialog-titlebar-close:focus {
+  padding: 0;
+}
+.ui-dialog .ui-dialog-content {
+  border: 0;
+  padding: 0.5em 1em;
+  background: none;
+  overflow: auto;
+  zoom: 1;
+}
+.ui-dialog .ui-dialog-buttonpane {
+  text-align: left;
+  border-width: 1px 0 0 0;
+  background-image: none;
+  margin: 0.5em 0 0 0;
+  padding: 0.3em 1em 0.5em 0.4em;
+}
+.ui-dialog .ui-dialog-buttonpane button {
+  float: right;
+  margin: 0.5em 0.4em 0.5em 0;
+  cursor: pointer;
+  padding: 0.2em 0.6em 0.3em 0.6em;
+  line-height: 1.4em;
+  width: auto;
+  overflow: visible;
+}
+.ui-dialog .ui-resizable-se {
+  width: 14px;
+  height: 14px;
+  right: 3px;
+  bottom: 3px;
+}
+.ui-draggable .ui-dialog-titlebar {
+  cursor: move;
+}
+.ui-slider {
+  position: relative;
+  text-align: left;
+}
+.ui-slider .ui-slider-handle {
+  position: absolute;
+  z-index: 2;
+  width: 1.2em;
+  height: 1.2em;
+  cursor: default;
+}
+.ui-slider .ui-slider-range {
+  position: absolute;
+  z-index: 1;
+  font-size: 0.7em;
+  display: block;
+  border: 0;
+}
+.ui-slider-horizontal {
+  height: 0.8em;
+}
+.ui-slider-horizontal .ui-slider-handle {
+  top: -0.3em;
+  margin-left: -0.6em;
+}
+.ui-slider-horizontal .ui-slider-range {
+  top: 0;
+  height: 100%;
+}
+.ui-slider-horizontal .ui-slider-range-min {
+  left: 0;
+}
+.ui-slider-horizontal .ui-slider-range-max {
+  right: 0;
+}
+.ui-slider-vertical {
+  width: 0.8em;
+  height: 100px;
+}
+.ui-slider-vertical .ui-slider-handle {
+  left: -0.3em;
+  margin-left: 0;
+  margin-bottom: -0.6em;
+}
+.ui-slider-vertical .ui-slider-range {
+  left: 0;
+  width: 100%;
+}
+.ui-slider-vertical .ui-slider-range-min {
+  bottom: 0;
+}
+.ui-slider-vertical .ui-slider-range-max {
+  top: 0;
+}
+.ui-tabs {
+  padding: 0.2em;
+  zoom: 1;
+}
+.ui-tabs .ui-tabs-nav {
+  list-style: none;
+  position: relative;
+  padding: 0.2em 0.2em 0;
+}
+.ui-tabs .ui-tabs-nav li {
+  position: relative;
+  float: left;
+  border-bottom-width: 0 !important;
+  margin: 0 0.2em -1px 0;
+  padding: 0;
+}
+.ui-tabs .ui-tabs-nav li a {
+  float: left;
+  text-decoration: none;
+  padding: 0.5em 1em;
+}
+.ui-tabs .ui-tabs-nav li.ui-tabs-selected {
+  padding-bottom: 1px;
+  border-bottom-width: 0;
+}
+.ui-tabs .ui-tabs-nav li.ui-tabs-selected a,
+.ui-tabs .ui-tabs-nav li.ui-state-disabled a,
+.ui-tabs .ui-tabs-nav li.ui-state-processing a {
+  cursor: text;
+}

+ 107 - 0
packages/fcl-css/tests/css/news.css

@@ -0,0 +1,107 @@
+body  { font-family:Verdana, Geneva, sans-serif; font-size:10pt; color:#828282; }
+td    { font-family:Verdana, Geneva, sans-serif; font-size:10pt; color:#828282; }
+
+.admin td   { font-family:Verdana, Geneva, sans-serif; font-size:8.5pt; color:#000000; }
+.subtext td { font-family:Verdana, Geneva, sans-serif; font-size:  7pt; color:#828282; }
+
+input    { font-family:monospace; font-size:10pt; }
+input[type=\"submit\"] { font-family:Verdana, Geneva, sans-serif; }
+textarea { font-family:monospace; font-size:10pt; }
+
+a:link    { color:#000000; text-decoration:none; }
+a:visited { color:#828282; text-decoration:none; }
+
+.default { font-family:Verdana, Geneva, sans-serif; font-size: 10pt; color:#828282; }
+.title   { font-family:Verdana, Geneva, sans-serif; font-size: 10pt; color:#828282; overflow:hidden; }
+.pagetop { font-family:Verdana, Geneva, sans-serif; font-size: 10pt; color:#222222; line-height:12px; }
+.comment { font-family:Verdana, Geneva, sans-serif; font-size:  9pt; }
+.hnname  { margin-right: 5px; }
+
+#hnmain { min-width: 796px; }
+
+.title a { word-break: break-word; }
+
+.comment a:link, .comment a:visited { text-decoration: underline; }
+.noshow { display: none; }
+.nosee { visibility: hidden; pointer-events: none; cursor: default }
+
+.c00, .c00 a:link { color:#000000; }
+
+.pagetop a:visited { color:#000000;}
+.topsel a:link, .topsel a:visited { color:#ffffff; }
+
+.subtext a:link, .subtext a:visited { color:#828282; }
+.subtext a:hover { text-decoration:underline; }
+
+.default p { margin-top: 8px; margin-bottom: 0px; }
+
+.pagebreak {page-break-before:always}
+
+pre { overflow: auto; padding: 2px; white-space: pre-wrap; overflow-wrap:anywhere; }
+pre:hover { overflow:auto }
+
+.votearrow {
+  width:      10px;
+  height:     10px;
+  border:     0px;
+  margin:     3px 2px 6px;
+  background: url("grayarrow.gif")
+  no-repeat;
+}
+
+.votelinks.nosee div.votearrow.rotate180 {
+  display: none;
+}
+
+table.padtab td { padding:0px 10px }
+
+@media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min-device-pixel-ratio: 2) {
+  .votearrow { background-size: 10px; background-image: url("grayarrow2x.gif"); }
+}
+
+.rotate180 {
+  -webkit-transform: rotate(180deg);  /* Chrome and other webkit browsers */
+  -moz-transform:    rotate(180deg);  /* FF */
+  -o-transform:      rotate(180deg);  /* Opera */
+  -ms-transform:     rotate(180deg);  /* IE9 */
+  transform:         rotate(180deg);  /* W3C complaint browsers */
+
+  /* IE8 and below */
+  -ms-filter: "progid:DXImageTransform.Microsoft.Matrix(M11=-1, M12=0, M21=0, M22=-1, DX=0, DY=0, SizingMethod='auto expand')";
+}
+
+/* mobile device */
+@media only screen
+and (min-width : 300px)
+and (max-width : 750px) {
+  #hnmain { width: 100%; min-width: 0; }
+  body { padding: 0; margin: 0; width: 100%; -webkit-text-size-adjust: none; }
+  td { height: inherit !important; }
+  .title, .comment { font-size: inherit;  }
+  span.pagetop { display: block; margin: 3px 5px; font-size: 12px; line-height: normal }
+  span.pagetop b { display: block; font-size: 15px; }
+  table.comment-tree .comment a { display: inline-block; max-width: 200px; overflow: hidden; white-space: nowrap;
+    text-overflow: ellipsis; vertical-align:top; }
+  img[src='s.gif'][width='40'] { width: 12px; }
+  img[src='s.gif'][width='840'] { width: 252px; }
+  .title { font-size: 11pt; line-height: 14pt;  }
+  .subtext { font-size: 9pt; }
+  .itemlist { padding-right: 5px;}
+  .votearrow { transform: scale(1.3,1.3); margin-right: 6px; }
+  .votearrow.rotate180 {
+    -webkit-transform: rotate(180deg) scale(1.3,1.3);  /* Chrome and other webkit browsers */
+    -moz-transform:    rotate(180deg) scale(1.3,1.3);  /* FF */
+    -o-transform:      rotate(180deg) scale(1.3,1.3);  /* Opera */
+    -ms-transform:     rotate(180deg) scale(1.3,1.3);  /* IE9 */
+    transform:         rotate(180deg) scale(1.3,1.3);  /* W3C complaint browsers */
+  }
+  .votelinks { min-width: 18px; }
+  .votelinks a { display: block; margin-bottom: 9px; }
+  input[type='text'], input[type='number'], textarea { font-size: 16px; width: 90%; }
+}
+
+.comment { max-width: 1215px; overflow-wrap:anywhere; }
+
+@media only screen and (min-width : 300px) and (max-width : 389px) {
+  .comment { max-width: 270px; overflow: hidden }
+}

+ 8 - 0
packages/fcl-css/tests/css/padding.css

@@ -0,0 +1,8 @@
+
+div {
+  padding: 25px 50px 75px 100px;
+}
+
+span {
+  padding: 8px;
+}

+ 206 - 0
packages/fcl-css/tests/css/style.css

@@ -0,0 +1,206 @@
+body:-webkit-full-page-media {
+    background-color: rgb(0, 0, 0)
+}
+
+
+html {
+    display: block;
+}
+
+head {
+    display: none
+}
+
+
+
+p {
+    display: block;
+    margin-inline-start: 0;
+    margin-inline-end: 0;
+}
+
+article, aside, footer, header, hgroup, main, nav, section {
+    display: block
+}
+
+marquee {
+    display: inline-block;
+    width: -webkit-fill-available;
+}
+
+address {
+    display: block
+}
+
+blockquote {
+    display: block;
+    margin-block-end: 1em;
+    margin-inline-start: 40px;
+    margin-inline-end: 40px;
+}
+
+figcaption {
+    display: block
+}
+
+figure {
+    display: block;
+    margin-block-start: 1em;
+    margin-block-end: 1em;
+    margin-inline-start: 40px;
+    margin-inline-end: 40px;
+}
+
+q {
+    display: inline
+}
+
+
+center {
+    display: block;
+    text-align: -webkit-center
+}
+
+hr {
+    display: block;
+    overflow: hidden;
+    unicode-bidi: isolate;
+    margin-block-start: 0.5em;
+    margin-block-end: 0.5em;
+    margin-inline-start: auto;
+    margin-inline-end: auto;
+    border-style: inset;
+    border-width: 1px
+}
+
+map {
+    display: inline
+}
+
+video {
+    object-fit: contain;
+}
+
+
+h1 {
+    display: block;
+    font-size: 2em;
+    margin-inline-start: 0;
+    margin-inline-end: 0;
+    font-weight: bold
+}
+
+h2 {
+    display: block;
+    font-size: 1.5em;
+    margin-block-end: 0.83em;
+    margin-inline-start: 0;
+    margin-inline-end: 0;
+    font-weight: bold
+}
+
+h3 {
+    display: block;
+    font-size: 1.17em;
+    margin-block-end: 1em;
+    margin-inline-start: 0;
+    margin-inline-end: 0;
+    font-weight: bold
+}
+
+h4 {
+    display: block;
+    margin-block-end: 1.33em;
+    margin-inline-start: 0;
+    margin-inline-end: 0;
+    font-weight: bold
+}
+
+h5 {
+    display: block;
+    font-size: .83em;
+    margin-block-end: 1.67em;
+    margin-inline-start: 0;
+    margin-inline-end: 0;
+    font-weight: bold
+}
+
+h6 {
+    display: block;
+    font-size: .67em;
+    margin-block-end: 2.33em;
+    margin-inline-start: 0;
+    margin-inline-end: 0;
+    font-weight: bold
+}
+
+
+table {
+    display: table;
+    border-collapse: separate;
+    border-spacing: 2px;
+    border-color: gray;
+    box-sizing: border-box;
+    text-indent: initial
+}
+
+thead {
+    display: table-header-group;
+    vertical-align: middle;
+    border-color: inherit
+}
+
+tbody {
+    display: table-row-group;
+    vertical-align: middle;
+    border-color: inherit
+}
+
+tfoot {
+    display: table-footer-group;
+    vertical-align: middle;
+    border-color: inherit
+}
+
+/*table > tr {
+    vertical-align: middle;
+}*/
+
+col {
+    display: table-column
+}
+
+colgroup {
+    display: table-column-group
+}
+
+tr {
+    display: table-row;
+    vertical-align: inherit;
+    border-color: inherit
+}
+
+td, th {
+    display: table-cell;
+    vertical-align: inherit
+}
+
+th {
+    font-weight: bold;
+    text-align: -internal-center
+}
+
+caption {
+    display: table-caption;
+    text-align: -webkit-center
+}
+
+
+ul, menu, dir {
+    display: block;
+    list-style-type: disc;
+    margin-block-end: 1em;
+    margin-inline-start: 0;
+    margin-inline-end: 0;
+    padding-inline-start: 40px
+}

+ 51 - 0
packages/fcl-css/tests/css/style2.css

@@ -0,0 +1,51 @@
+input[type="search" i]::-webkit-search-cancel-button {
+    appearance: auto;
+    display: block;
+    cursor: default;
+    flex: none;
+    -webkit-user-modify: read-only !important;
+    margin-inline-start: 1px;
+    opacity: 0;
+    pointer-events: none;
+    user-select: none !important;
+}
+
+
+input[type="button" i], input[type="submit" i], input[type="reset" i], input[type="file" i]::-webkit-file-upload-button, button {
+    align-items: flex-start;
+    text-align: center;
+    cursor: default;
+    color: -internal-light-dark(ButtonText, #AAAAAA);
+    padding: 2px 6px 3px 6px;
+    border: 2px outset ButtonFace;
+    background-color: ButtonFace;
+    box-sizing: border-box
+}
+
+input[type="button" i]:disabled, input[type="submit" i]:disabled, input[type="reset" i]:disabled,
+input[type="file" i]:disabled::-webkit-file-upload-button, button:disabled,
+select:disabled, optgroup:disabled, option:disabled,
+select[disabled]>option {
+    color: -internal-light-dark(GrayText, #aaa);
+}
+
+input[type="date" i]:disabled::-webkit-clear-button,
+input[type="date" i]:disabled::-webkit-inner-spin-button,
+input[type="datetime-local" i]:disabled::-webkit-clear-button,
+input[type="datetime-local" i]:disabled::-webkit-inner-spin-button,
+input[type="month" i]:disabled::-webkit-clear-button,
+input[type="month" i]:disabled::-webkit-inner-spin-button,
+input[type="week" i]:disabled::-webkit-clear-button,
+input[type="week" i]:disabled::-webkit-inner-spin-button,
+input:disabled::-webkit-calendar-picker-indicator,
+input[type="date" i][readonly]::-webkit-clear-button,
+input[type="date" i][readonly]::-webkit-inner-spin-button,
+input[type="datetime-local" i][readonly]::-webkit-clear-button,
+input[type="datetime-local" i][readonly]::-webkit-inner-spin-button,
+input[type="month" i][readonly]::-webkit-clear-button,
+input[type="month" i][readonly]::-webkit-inner-spin-button,
+input[type="week" i][readonly]::-webkit-clear-button,
+input[type="week" i][readonly]::-webkit-inner-spin-button,
+input[readonly]::-webkit-calendar-picker-indicator {
+    visibility: hidden;
+}

+ 175 - 0
packages/fcl-css/tests/css/style_big.css

@@ -0,0 +1,175 @@
+@namespace "http://www.w3.org/1999/xhtml";
+
+html {
+    display: block;
+}
+
+body {
+    display: block;
+    margin: 8px
+}
+
+body:-webkit-full-page-media {
+    background-color: rgb(0, 0, 0)
+}
+
+p {
+    display: block;
+    margin-block-start: 1__qem;
+    margin-block-end: 1__qem;
+    margin-inline-start: 0;
+    margin-inline-end: 0;
+}
+
+article, aside, footer, header, hgroup, main, nav, section {
+    display: block
+}
+
+marquee {
+    display: inline-block;
+    width: -webkit-fill-available;
+}
+
+
+blockquote {
+    display: block;
+    margin-block-start: 1__qem;
+    margin-block-end: 1em;
+    margin-inline-start: 40px;
+    margin-inline-end: 40px;
+}
+
+q:before {
+    content: open-quote;
+}
+
+q:after {
+    content: close-quote;
+}
+
+center {
+    display: block;
+    /* special centering to be able to emulate the html4/netscape behaviour */
+    text-align: -webkit-center
+}
+
+
+
+:-webkit-any(article,aside,nav,section) h1 {
+    font-size: 1.5em;
+    margin-block-start: 0.83__qem;
+    margin-block-end: 0.83em;
+}
+
+table > tr {
+    vertical-align: middle;
+}
+
+td, th {
+    display: table-cell;
+    vertical-align: inherit
+}
+
+th {
+    font-weight: bold;
+    text-align: -internal-center
+}
+
+caption {
+    display: table-caption;
+    text-align: -webkit-center
+}
+
+
+:-webkit-any(table, thead, tbody, tfoot, tr) > form:-internal-is-html {
+  display: none !important;
+}
+
+input[type="search" i] {
+    appearance: auto;
+    box-sizing: border-box;
+}
+input[type="search" i]::-webkit-search-cancel-button {
+    appearance: auto;
+    display: block;
+    cursor: default;
+    flex: none;
+    -webkit-user-modify: read-only !important;
+    margin-inline-start: 1px;
+    opacity: 0;
+    pointer-events: none;
+    user-select: none !important;
+}
+
+
+input:enabled:read-write:-webkit-any(:focus,:hover)::-webkit-inner-spin-button {
+    opacity: 1;
+    pointer-events: auto;
+}
+
+::-webkit-input-placeholder {
+    -webkit-text-security: none;
+    color: #757575;
+    direction: inherit !important;
+    pointer-events: none !important;
+    text-orientation: inherit !important;
+    writing-mode: inherit !important;
+}
+
+input::-webkit-input-placeholder {
+    text-overflow: inherit;
+    line-height: initial;
+    white-space: pre;
+    word-wrap: normal;
+    overflow: hidden;
+    -webkit-user-modify: read-only !important;
+}
+
+
+input[type="password" i] {
+    -webkit-text-security: disc !important;
+}
+
+input[type="password" i]::-internal-input-suggested {
+    -webkit-text-security: disc !important;
+}
+
+input[type="hidden" i], input[type="image" i], input[type="file" i] {
+    -webkit-appearance: initial; /* AutoAppearanceFor() should match to this. */
+    padding: initial;
+    background-color: initial;
+    border: initial;
+    cursor: default;
+}
+
+
+input[type="button" i]:focus,
+input[type="checkbox" i]:focus,
+input[type="file" i]:focus,
+input[type="hidden" i]:focus,
+input[type="image" i]:focus,
+input[type="radio" i]:focus,
+input[type="reset" i]:focus,
+input[type="submit" i]:focus,
+input[type="file" i]:focus::-webkit-file-upload-button {
+    outline-offset: 0
+}
+
+dialog:not([open]) {
+    display: none
+}
+
+@page {
+    /* FIXME: Define the right default values for page properties. */
+    size: auto;
+    margin: auto;
+    padding: 0px;
+    border-width: 0px;
+}
+
+/* Allows thead sections to print at the top of each page. */
+@media print {
+    thead { break-inside:avoid; }
+}
+
+/* noscript is handled internally, as it depends on settings. */

+ 5 - 0
packages/fcl-css/tests/css/wildcard.css

@@ -0,0 +1,5 @@
+* {
+    margin: 0;
+    padding: 0;
+    box-sizing: border-box;
+}

+ 807 - 0
packages/fcl-css/tests/tccssparser.pp

@@ -0,0 +1,807 @@
+{
+    This file is part of the Free Pascal Run time library.
+    Copyright (c) 2022- by Michael Van Canneyt ([email protected])
+
+    This file contains the tests for the CSS parser
+
+    See the File COPYING.FPC, included in this distribution,
+    for details about the copyright.
+
+    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.
+
+ **********************************************************************}
+unit tcCSSParser;
+
+{$mode objfpc}{$H+}
+
+interface
+
+uses
+  Classes, SysUtils, fpcunit, testutils, testregistry, fpcssparser, fpcsstree;
+
+type
+
+  { TTestBaseCSSParser }
+
+  TTestBaseCSSParser = class(TTestCase)
+  Private
+    FParseResult: TCSSElement;
+    FSource : TStringStream;
+    FParser : TCSSParser;
+    FToFree: TCSSElement;
+    procedure clear;
+    function GetRule: TCSSRuleElement;
+  protected
+    procedure SetUp; override;
+    procedure TearDown; override;
+    Procedure CreateParser(Const ASource : string);
+    procedure Parse;
+    procedure Parse(Const aSource : String);
+    function ParseRule(Const aSource : String) :  TCSSRuleElement;
+    procedure AssertEquals(AMessage: String; AExpected, AActual: TCSSUnits);   overload;
+    procedure AssertEquals(AMessage: String; AExpected, AActual: TCSSBinaryOperation);   overload;
+    Function CheckClass(Const aMsg : String; aExpectedClass : TCSSElementClass; aActual : TCSSElement) : TCSSElement;
+    Function CheckDeclaration(aRule : TCSSRuleElement; aIndex : Integer) : TCSSDeclarationElement;
+    Function CheckDeclaration(aRule : TCSSRuleElement; aIndex : Integer; const AKey : String) : TCSSDeclarationElement;
+    Function CheckSelector(aRule : TCSSRuleElement; aIndex : Integer) : TCSSElement;
+    Function CheckSelector(aRule : TCSSRuleElement; aIndex : Integer; const aName : String) : TCSSElement;
+    function CheckLiteral(Msg: String; aEl: TCSSelement; aValue: String) : TCSSStringElement; overload;
+    function CheckLiteral(Msg: String; aEl: TCSSelement; aValue: Integer) : TCSSIntegerElement;  overload;
+    function CheckLiteral(Msg: String; aEl: TCSSelement; aValue: Integer; AUnits : TCSSUnits) : TCSSIntegerElement;  overload;
+    Function GetCalArg(aCall : TCSSCallElement; aIndex : Integer) : TCSSElement;
+  Public
+    Property ParseResult : TCSSElement read FParseResult;
+    Property FirstRule : TCSSRuleElement Read GetRule;
+    Property ToFree : TCSSElement Read FToFree Write FToFree;
+  end;
+
+  { TTestCSSParser }
+
+  TTestCSSParser = class(TTestBaseCSSParser)
+  private
+  Published
+    Procedure TestEmpty;
+    Procedure TestEmptyRule;
+    Procedure TestPrefixedEmptyRule;
+    Procedure TestClassPrefixedEmptyRule;
+    Procedure TestHashPrefixedEmptyRule;
+    procedure TestDoublePrefixedEmptyRule;
+    procedure TestDoubleMixedPrefixedEmptyRule;
+    procedure TestAttributePrefixedEmptyRule;
+    procedure TestFuncPrefixedEmptyRule;
+    procedure TestQueryPrefixedEmptyRule;
+    Procedure TestCommaPrefixedEmptyRule;
+    Procedure TestOneDeclarationIDValue;
+    Procedure TestOneDeclarationIDValueAndEmpty;
+    Procedure TestOneDeclarationIntValue;
+    Procedure TestOneDeclarationStringValue;
+    Procedure TestOneDeclarationHashValue;
+    Procedure TestOneDeclarationURLValue;
+    Procedure TestOneDeclarationMultiValue;
+    Procedure TestOneDeclarationMultiListValue;
+    Procedure TestOneDeclarationExprValue;
+    Procedure TestOneDeclarationUnicodeRangeValue;
+    Procedure TestOneDeclarationNoColon;
+    Procedure TestTwoDeclarationNoColon;
+    Procedure TestOneEmptyDeclaration;
+    Procedure TestImportAtKeyWord;
+    Procedure TestMediaPrint;
+  end;
+
+  { TTestCSSFilesParser }
+
+  TTestCSSFilesParser = class(TTestBaseCSSParser)
+  private
+    FTestDir: String;
+    procedure SetTestDir(AValue: String);
+  Public
+    Procedure SetUp;override;
+    Procedure RunFileTest(aFile : String='');
+    Property TestDir : String Read FTestDir Write SetTestDir;
+  Published
+    // lowercase name must match 'test'+filename
+    Procedure Testabsolute;
+    Procedure Testanimation;
+    Procedure Testanon;
+    Procedure Testbigbig;
+    Procedure Testclass;
+    Procedure Testcolor;
+    Procedure Testfont_face;
+    Procedure Testfont_face2;
+    Procedure Testfont;
+    Procedure Testhello;
+    Procedure Testid;
+    Procedure Testinput_type;
+    Procedure Testmargin;
+    Procedure Testmedia_query;
+    Procedure Testmystyle;
+    Procedure Testnews;
+    Procedure Testpadding;
+    Procedure Teststyle;
+    Procedure Teststyle2;
+    Procedure Teststyle_big;
+    Procedure TesTwildcard;
+  end;
+
+implementation
+
+uses inifiles, typinfo;
+
+{ TTestCSSFilesParser }
+
+procedure TTestCSSFilesParser.SetTestDir(AValue: String);
+begin
+  if FTestDir=AValue then Exit;
+  FTestDir:=AValue;
+end;
+
+procedure TTestCSSFilesParser.SetUp;
+begin
+  inherited SetUp;
+  With TMemIniFile.Create(ChangeFileExt(Paramstr(0),'.ini')) do
+    try
+      TestDir:=ReadString('CSS','SourceDir','css');
+    finally
+      Free
+    end;
+end;
+
+procedure TTestCSSFilesParser.RunFileTest(aFile: String);
+
+var
+  fn : string;
+  OK : Boolean;
+
+begin
+  if Afile='' then
+    begin
+    aFile:=LowerCase(TestName);
+    if Copy(aFile,1,4)='test' then
+      Delete(aFile,1,4);
+    end;
+  OK:=False;
+  With TStringList.Create do
+    try
+      fn:=IncludeTrailingPathDelimiter(TestDir)+aFile+'.css';
+      fn:=ExpandFileName(FN);
+      // Writeln('Analysing file ',FN);
+      LoadFromFile(fn);
+      Parse(Text);
+      OK:=True;
+    finally
+      if not OK then
+        begin
+        Writeln('Source generating error: ',FN);
+        Writeln(Text);
+        end;
+      Free;
+    end;
+end;
+
+
+procedure TTestCSSFilesParser.Testabsolute;
+begin
+  RunFileTest;
+end;
+
+procedure TTestCSSFilesParser.Testanimation;
+begin
+  RunFileTest;
+end;
+
+procedure TTestCSSFilesParser.Testanon;
+begin
+  RunFileTest;
+end;
+
+procedure TTestCSSFilesParser.Testbigbig;
+begin
+  RunFileTest;
+end;
+
+procedure TTestCSSFilesParser.Testclass;
+begin
+  RunFileTest;
+end;
+
+procedure TTestCSSFilesParser.Testcolor;
+begin
+  RunFileTest;
+end;
+
+procedure TTestCSSFilesParser.Testfont_face;
+begin
+  RunFileTest('font-face');
+end;
+
+procedure TTestCSSFilesParser.Testfont_face2;
+begin
+  RunFileTest('font-face2');
+end;
+
+procedure TTestCSSFilesParser.Testfont;
+begin
+  RunFileTest;
+end;
+
+procedure TTestCSSFilesParser.Testhello;
+begin
+  RunFileTest;
+end;
+
+procedure TTestCSSFilesParser.Testid;
+begin
+  RunFileTest;
+end;
+
+procedure TTestCSSFilesParser.Testinput_type;
+begin
+  RunFileTest;
+end;
+
+procedure TTestCSSFilesParser.Testmargin;
+begin
+  RunFileTest;
+end;
+
+procedure TTestCSSFilesParser.Testmedia_query;
+begin
+  RunFileTest;
+end;
+
+procedure TTestCSSFilesParser.Testmystyle;
+begin
+  RunFileTest;
+end;
+
+procedure TTestCSSFilesParser.Testnews;
+begin
+  RunFileTest;
+end;
+
+procedure TTestCSSFilesParser.Testpadding;
+begin
+  RunFileTest;
+end;
+
+procedure TTestCSSFilesParser.Teststyle;
+begin
+  RunFileTest;
+end;
+
+procedure TTestCSSFilesParser.Teststyle2;
+begin
+  RunFileTest;
+end;
+
+procedure TTestCSSFilesParser.Teststyle_big;
+begin
+  RunFileTest;
+end;
+
+procedure TTestCSSFilesParser.TesTwildcard;
+begin
+  RunFileTest;
+end;
+
+{ TTestCSSParser }
+
+procedure TTestCSSParser.TestEmpty;
+var
+  L : TCSSCompoundElement;
+begin
+  Parse('');
+  L:=TCSSCompoundElement(CheckClass('list',TCSSCompoundElement,ParseResult));
+  AssertEquals('No children',0,L.ChildCount);
+end;
+
+procedure TTestCSSParser.TestEmptyRule;
+
+var
+  R : TCSSRuleElement;
+begin
+  R:=ParseRule('{}');
+  AssertEquals('No rule children',0,R.ChildCount);
+end;
+
+procedure TTestCSSParser.TestPrefixedEmptyRule;
+
+var
+  R : TCSSRuleElement;
+  sel: TCSSIdentifierElement;
+
+begin
+  ParseRule('a { }');
+  R:=TCSSRuleElement(CheckClass('Rule',TCSSRuleElement,FirstRule));
+  AssertEquals('No rule children',0,R.ChildCount);
+  AssertEquals('selector count',1,R.SelectorCount);
+  sel:=TCSSIdentifierElement(CheckClass('Selector', TCSSIdentifierElement,R.Selectors[0]));
+  AssertEquals('Sel name','a',Sel.Value);
+end;
+
+procedure TTestCSSParser.TestClassPrefixedEmptyRule;
+
+var
+  R : TCSSRuleElement;
+  sel: TCSSClassNameElement;
+
+begin
+  ParseRule('.a { }');
+  R:=TCSSRuleElement(CheckClass('Rule',TCSSRuleElement,FirstRule));
+  AssertEquals('No rule children',0,R.ChildCount);
+  AssertEquals('selector count',1,R.SelectorCount);
+  sel:=TCSSClassNameElement(CheckClass('Selector', TCSSClassNameElement,R.Selectors[0]));
+  AssertEquals('Sel name','.a',Sel.Value);
+end;
+
+procedure TTestCSSParser.TestHashPrefixedEmptyRule;
+var
+  R : TCSSRuleElement;
+  sel: TCSSStringElement;
+
+begin
+  ParseRule('#a { }');
+  R:=TCSSRuleElement(CheckClass('Rule',TCSSRuleElement,FirstRule));
+  AssertEquals('No rule children',0,R.ChildCount);
+  AssertEquals('selector count',1,R.SelectorCount);
+  sel:=TCSSStringElement(CheckClass('Selector', TCSSStringElement,R.Selectors[0]));
+  AssertEquals('Sel name','#a',Sel.Value);
+end;
+
+procedure TTestCSSParser.TestDoublePrefixedEmptyRule;
+
+var
+  R : TCSSRuleElement;
+  sel: TCSSIdentifierElement;
+
+begin
+  ParseRule('a b { }');
+  R:=TCSSRuleElement(CheckClass('Rule',TCSSRuleElement,FirstRule));
+  AssertEquals('No rule children',0,R.ChildCount);
+  AssertEquals('selector count',2,R.SelectorCount);
+  sel:=TCSSIdentifierElement(CheckClass('Selector', TCSSIdentifierElement,R.Selectors[0]));
+  AssertEquals('Sel name','a',Sel.Value);
+  sel:=TCSSIdentifierElement(CheckClass('Selector', TCSSIdentifierElement,R.Selectors[1]));
+  AssertEquals('Sel name','b',Sel.Value);
+end;
+
+procedure TTestCSSParser.TestDoubleMixedPrefixedEmptyRule;
+
+var
+  R : TCSSRuleElement;
+  sel: TCSSIdentifierElement;
+  sel2: TCSSClassNameElement;
+
+begin
+  ParseRule('a .b { }');
+  R:=TCSSRuleElement(CheckClass('Rule',TCSSRuleElement,FirstRule));
+  AssertEquals('No rule children',0,R.ChildCount);
+  AssertEquals('selector count',2,R.SelectorCount);
+  sel:=TCSSIdentifierElement(CheckClass('Selector', TCSSIdentifierElement,R.Selectors[0]));
+  AssertEquals('Sel name','a',Sel.Value);
+  sel2:=TCSSClassNameElement(CheckClass('Selector', TCSSClassNameElement,R.Selectors[1]));
+  AssertEquals('Sel name','.b',Sel2.Value);
+end;
+
+procedure TTestCSSParser.TestAttributePrefixedEmptyRule;
+var
+  R : TCSSRuleElement;
+  sel: TCSSArrayElement;
+  id : TCSSIdentifierElement;
+  bin : TCSSBinaryElement;
+
+begin
+  ParseRule('a[b="c"] { }');
+  R:=TCSSRuleElement(CheckClass('Rule',TCSSRuleElement,FirstRule));
+  AssertEquals('No rule children',0,R.ChildCount);
+  AssertEquals('selector count',1,R.SelectorCount);
+  sel:=TCSSArrayElement(CheckClass('Selector', TCSSArrayElement,R.Selectors[0]));
+  Id:=TCSSIdentifierElement(CheckClass('Array prefix',TCSSIdentifierElement,Sel.Prefix));
+  AssertEquals('Prefix name','a',Id.Value);
+  AssertEquals('Array count',1,Sel.ChildCount);
+  Bin:=TCSSBinaryElement(CheckClass('Bin',TCSSBinaryElement,sel.children[0]));
+  AssertEquals('Binary op',boEquals,Bin.Operation);
+end;
+
+procedure TTestCSSParser.TestFuncPrefixedEmptyRule;
+
+var
+  R : TCSSRuleElement;
+  sel: TCSSArrayElement;
+
+begin
+  R:=ParseRule('input:enabled:read-write:-webkit-any(:focus,:hover)::-webkit-clear-button {  }');
+  AssertEquals('No rule children',0,R.ChildCount);
+  AssertEquals('selector count',5,R.SelectorCount);
+  CheckSelector(R,0,'input');
+  CheckSelector(R,1,':enabled');
+  CheckSelector(R,2,':read-write');
+  CheckSelector(R,4,'::-webkit-clear-button');
+end;
+
+procedure TTestCSSParser.TestQueryPrefixedEmptyRule;
+var
+  R : TCSSRuleElement;
+begin
+  R:=ParseRule('@media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min-device-pixel-ratio: 3) { }');
+end;
+
+procedure TTestCSSParser.TestCommaPrefixedEmptyRule;
+var
+  R : TCSSRuleElement;
+  sel: TCSSArrayElement;
+
+begin
+  R:=ParseRule('#facebox .tl,#facebox .tl { }');
+end;
+
+procedure TTestCSSParser.TestOneDeclarationIDValue;
+var
+  R : TCSSRuleElement;
+  D : TCSSDeclarationElement;
+  Id : TCSSIdentifierElement;
+
+begin
+  R:=ParseRule('{ a : b; }');
+  AssertEquals('selector count',0,R.SelectorCount);
+  D:=CheckDeclaration(R,0,'a');
+  AssertEquals('Value count', 1, D.ChildCount);
+  ID:=TCSSIdentifierElement(CheckClass('Value', TCSSIdentifierElement,D.Children[0]));
+  AssertEquals('Value','b',id.Value);
+end;
+
+procedure TTestCSSParser.TestOneDeclarationIDValueAndEmpty;
+var
+  R : TCSSRuleElement;
+  D : TCSSDeclarationElement;
+  Id : TCSSIdentifierElement;
+
+begin
+  R:=ParseRule('{ a : b;; }');
+  AssertEquals('selector count',0,R.SelectorCount);
+  D:=CheckDeclaration(R,0,'a');
+  AssertEquals('Value count', 1, D.ChildCount);
+  ID:=TCSSIdentifierElement(CheckClass('Value', TCSSIdentifierElement,D.Children[0]));
+  AssertEquals('Value','b',id.Value);
+end;
+
+procedure TTestCSSParser.TestOneDeclarationIntValue;
+var
+  R : TCSSRuleElement;
+  D : TCSSDeclarationElement;
+  U : TCSSUnits;
+
+begin
+  For U in TCSSUnits do
+    begin
+    R:=ParseRule('{ a : 1'+CSSUnitNames[U]+'; }');
+    AssertEquals('selector count',0,R.SelectorCount);
+    D:=CheckDeclaration(R,0,'a');
+    AssertEquals('Value count', 1, D.ChildCount);
+    CheckLiteral('Value for '+CSSUnitNames[U],D.Children[0],1,U);
+    end;
+end;
+
+procedure TTestCSSParser.TestOneDeclarationStringValue;
+var
+  R : TCSSRuleElement;
+  D : TCSSDeclarationElement;
+
+begin
+  R:=ParseRule('{ a : "b"; }');
+  AssertEquals('selector count',0,R.SelectorCount);
+  D:=CheckDeclaration(R,0,'a');
+  AssertEquals('Value count', 1, D.ChildCount);
+  CheckLiteral('Value',D.Children[0],'b');
+end;
+
+procedure TTestCSSParser.TestOneDeclarationHashValue;
+
+var
+  R : TCSSRuleElement;
+  D : TCSSDeclarationElement;
+  S : TCSSStringElement;
+
+begin
+  R:=ParseRule('{ a : #ABABAB; }');
+  AssertEquals('selector count',0,R.SelectorCount);
+  D:=CheckDeclaration(R,0,'a');
+  AssertEquals('Value count', 1, D.ChildCount);
+  S:=TCSSStringElement(CheckClass('Value', TCSSStringElement,D.Children[0]));
+  AssertEquals('Value ','#ABABAB',S.Value);
+end;
+
+procedure TTestCSSParser.TestOneDeclarationURLValue;
+var
+  R : TCSSRuleElement;
+  D : TCSSDeclarationElement;
+  U : TCSSURLElement;
+
+begin
+  R:=ParseRule('{ a : url("b.c"); }');
+  AssertEquals('selector count',0,R.SelectorCount);
+  D:=CheckDeclaration(R,0,'a');
+  AssertEquals('Value count', 1, D.ChildCount);
+  U:=TCSSURLElement(CheckClass('Value', TCSSURLElement,D.Children[0]));
+  AssertEquals('Value ','b.c',U.Value);
+end;
+
+procedure TTestCSSParser.TestOneDeclarationMultiValue;
+var
+  R : TCSSRuleElement;
+  D : TCSSDeclarationElement;
+  L : TCSSListElement;
+
+begin
+  R:=ParseRule('{ a : 1px 2px 3px 4px; }');
+  AssertEquals('selector count',0,R.SelectorCount);
+  D:=CheckDeclaration(R,0,'a');
+  AssertEquals('Value count', 1, D.ChildCount);
+  L:=TCSSListElement(CheckClass('List',TCSSListElement,D.Children[0]));
+  AssertEquals('List element count', 4, L.ChildCount);
+  CheckLiteral('Value 1 ',L.Children[0],1,cuPX);
+  CheckLiteral('Value 2 ',L.Children[1],2,cuPX);
+  CheckLiteral('Value 3 ',L.Children[2],3,cuPX);
+  CheckLiteral('Value 4 ',L.Children[3],4,cuPX);
+end;
+
+procedure TTestCSSParser.TestOneDeclarationMultiListValue;
+var
+  R : TCSSRuleElement;
+  D : TCSSDeclarationElement;
+  L : TCSSListElement;
+
+begin
+  R:=ParseRule('{ a : 1px 2px, 3px 4px; }');
+  AssertEquals('selector count',0,R.SelectorCount);
+  D:=CheckDeclaration(R,0,'a');
+  AssertEquals('Value count', 2, D.ChildCount);
+  L:=TCSSListElement(CheckClass('List',TCSSListElement,D.Children[0]));
+  AssertEquals('List element count', 2, L.ChildCount);
+  CheckLiteral('Value 1 ',L.Children[0],1,cuPX);
+  CheckLiteral('Value 2 ',L.Children[1],2,cuPX);
+  L:=TCSSListElement(CheckClass('List',TCSSListElement,D.Children[1]));
+  AssertEquals('List element count', 2, L.ChildCount);
+  CheckLiteral('Value 3 ',L.Children[0],3,cuPX);
+  CheckLiteral('Value 4 ',L.Children[1],4,cuPX);
+end;
+
+procedure TTestCSSParser.TestOneDeclarationExprValue;
+begin
+  // Todo
+end;
+
+procedure TTestCSSParser.TestOneDeclarationUnicodeRangeValue;
+var
+  R : TCSSRuleElement;
+  D : TCSSDeclarationElement;
+begin
+  R:=ParseRule('{ unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; }');
+  D:=CheckDeclaration(R,0);
+  AssertEquals('Count values', 4, D.ChildCount);
+  CheckClass('Value 0',TCSSUnicodeRangeElement,D.Children[0]);
+  CheckClass('Value 1',TCSSUnicodeRangeElement,D.Children[1]);
+  CheckClass('Value 2',TCSSUnicodeRangeElement,D.Children[2]);
+  CheckClass('Value 3',TCSSUnicodeRangeElement,D.Children[3]);
+end;
+
+procedure TTestCSSParser.TestOneDeclarationNoColon;
+
+Var
+  R : TCSSRuleElement;
+
+begin
+  R:=ParseRule('@a b { 0% { d: e; } }');
+end;
+
+procedure TTestCSSParser.TestTwoDeclarationNoColon;
+Var
+  R : TCSSRuleElement;
+
+begin
+  R:=ParseRule('@a b { 0% { d: e; } 100% { f : g; }  }');
+end;
+
+procedure TTestCSSParser.TestOneEmptyDeclaration;
+var
+  R : TCSSRuleElement;
+begin
+  R:=ParseRule('{ ; }');
+  AssertEquals('selector count',0,R.SelectorCount);
+  AssertEquals('declaration count',0,R.ChildCount);
+
+end;
+
+procedure TTestCSSParser.TestImportAtKeyWord;
+var
+  R : TCSSAtRuleElement;
+//  D : TCSSDeclarationElement;
+begin
+  R:=TCSSAtRuleElement(CheckClass('at',TCSSAtRuleElement,ParseRule('@import url("abc.css");')));
+  AssertEquals('selector count',1,R.SelectorCount);
+  AssertEquals('declaration count',0,R.ChildCount);
+
+end;
+
+procedure TTestCSSParser.TestMediaPrint;
+begin
+  ParseRule('@media print { *, *:before {} }');
+end;
+
+
+{ TTestBaseCSSParser }
+
+function TTestBaseCSSParser.GetRule: TCSSRuleElement;
+var
+  L : TCSSCompoundElement;
+begin
+  L:=TCSSCompoundElement(CheckClass('list',TCSSCompoundElement,ParseResult));
+  AssertTrue('Result has at least 1 child',L.ChildCount>0);
+  if L.Children[0] is TCSSAtRuleElement then
+    Result:=TCSSAtRuleElement(CheckClass('First element is rule',TCSSAtRuleElement,L.Children[0]))
+  else
+    Result:=TCSSRuleElement(CheckClass('First element is rule',TCSSRuleElement,L.Children[0]));
+end;
+
+procedure TTestBaseCSSParser.SetUp;
+begin
+  inherited SetUp;
+  FParser:=Nil;
+  FSource:=Nil;
+end;
+
+procedure TTestBaseCSSParser.clear;
+
+begin
+  if FParseResult<>FToFree then
+    FreeAndNil(FToFree);
+  FreeAndNil(FParseResult);
+  FreeAndNil(FParser);
+  FReeAndNil(FSource);
+end;
+
+procedure TTestBaseCSSParser.TearDown;
+begin
+  Clear;
+  inherited TearDown;
+end;
+
+procedure TTestBaseCSSParser.CreateParser(const ASource: string);
+begin
+  Clear;
+  FSource:=TStringStream.Create(ASource);
+  FParser:=TCSSParser.Create(FSource);
+end;
+
+procedure TTestBaseCSSParser.Parse;
+begin
+  FParseResult:=FParser.Parse;
+  FToFree:=FParseResult;
+end;
+
+procedure TTestBaseCSSParser.Parse(const aSource: String);
+begin
+  CreateParser(aSource);
+  Parse;
+end;
+
+function TTestBaseCSSParser.ParseRule(const aSource: String): TCSSRuleElement;
+begin
+  Parse(aSource);
+  if ParseResult is TCSSRuleElement then
+    Result:=ParseResult as TCSSRuleElement
+  else
+    Result:=FirstRule;
+end;
+
+procedure TTestBaseCSSParser.AssertEquals(AMessage : String; AExpected, AActual: TCSSUnits);
+
+Var
+  S,EN1,EN2 : String;
+
+begin
+  If (AActual<>AExpected) then
+    begin
+    EN1:=GetEnumName(TypeINfo(TCSSUnits),Ord(AExpected));
+    EN2:=GetEnumName(TypeINfo(TCSSUnits),Ord(AActual));
+    S:=Format('%s : %s <> %s',[AMessage,EN1,EN2]);
+    Fail(S);
+    end;
+end;
+
+procedure TTestBaseCSSParser.AssertEquals(AMessage: String; AExpected, AActual: TCSSBinaryOperation);
+Var
+  S,EN1,EN2 : String;
+
+begin
+  If (AActual<>AExpected) then
+    begin
+    EN1:=GetEnumName(TypeINfo(TCSSBinaryOperation),Ord(AExpected));
+    EN2:=GetEnumName(TypeINfo(TCSSBinaryOperation),Ord(AActual));
+    S:=Format('%s : %s <> %s',[AMessage,EN1,EN2]);
+    Fail(S);
+    end;
+end;
+
+
+function TTestBaseCSSParser.CheckClass(const aMsg: String; aExpectedClass: TCSSElementClass; aActual: TCSSElement): TCSSElement;
+begin
+  AssertNotNull(aMsg+': Not null element',aExpectedClass);
+  AssertNotNull(aMsg+': Not null class',aActual);
+  AssertEquals(aMsg,aExpectedClass,aActual.ClassType);
+  Result:=aActual;
+end;
+
+function TTestBaseCSSParser.CheckDeclaration(aRule: TCSSRuleElement; aIndex: Integer): TCSSDeclarationElement;
+begin
+  AssertTrue('Have rule child '+IntToStr(aIndex),aIndex<aRule.ChildCount);
+  Result:=TCSSDeclarationElement(CheckClass('Decl', TCSSDeclarationElement,aRule.Children[aIndex]));
+end;
+
+function TTestBaseCSSParser.CheckDeclaration(aRule: TCSSRuleElement; aIndex: Integer; const AKey: String): TCSSDeclarationElement;
+
+var
+  ID : TCSSIdentifierElement;
+
+begin
+  Result:=CheckDeclaration(aRule,aIndex);
+  AssertEquals('Key count', 1, Result.KeyCount);
+  ID:=TCSSIdentifierElement(CheckClass('key 0', TCSSIdentifierElement,Result.Keys[0]));
+  AssertEquals('Key 0  name',aKey,id.Value);
+end;
+
+function TTestBaseCSSParser.CheckSelector(aRule: TCSSRuleElement; aIndex: Integer): TCSSElement;
+begin
+  AssertTrue('Have rule selector '+IntToStr(aIndex),aIndex<aRule.SelectorCount);
+  Result:=aRule.Selectors[aIndex];
+  AssertNotNull('Have selector non-nil',Result);
+end;
+
+function TTestBaseCSSParser.CheckSelector(aRule: TCSSRuleElement; aIndex: Integer; const aName: String): TCSSElement;
+begin
+  Result:=CheckSelector(aRule,aIndex);
+  if Result is TCSSIdentifierElement then
+    AssertEquals('Selector '+IntToStr(aIndex)+'name',aName,TCSSIdentifierElement(Result).Name)
+  else if Result is TCSSStringElement then
+    AssertEquals('Selector '+IntToStr(aIndex)+'name',aName,TCSSStringElement(Result).Value)
+  else
+    Fail('Selector '+IntToStr(aIndex)+' has no known type')
+
+
+end;
+
+function TTestBaseCSSParser.CheckLiteral(Msg: String; aEl: TCSSelement; aValue: String): TCSSStringElement;
+
+begin
+  Result:=TCSSStringElement(CheckClass(Msg+': class', TCSSStringElement,aEl));
+  AssertEquals(Msg+': String Value',aValue,Result.Value);
+end;
+
+function TTestBaseCSSParser.CheckLiteral(Msg: String; aEl: TCSSelement; aValue: Integer): TCSSIntegerElement;
+begin
+  Result:=TCSSIntegerElement(CheckClass(Msg+': Class', TCSSIntegerElement,aEl));
+  AssertEquals(Msg+': Value ',aValue,Result.Value);
+
+end;
+
+function TTestBaseCSSParser.CheckLiteral(Msg: String; aEl: TCSSelement; aValue: Integer; AUnits: TCSSUnits): TCSSIntegerElement;
+begin
+  Result:=CheckLiteral(Msg,aEl,aValue);
+  AssertEquals('Units',aUnits,Result.Units);
+end;
+
+function TTestBaseCSSParser.GetCalArg(aCall: TCSSCallElement; aIndex: Integer): TCSSElement;
+begin
+  AssertNotNull('Have call element',aCall);
+  AssertTrue('Have argument '+IntToStr(aIndex),aIndex<aCall.ChildCount);
+  Result:=aCall.Children[0];
+  AssertNotNull('Have call argument',Result);
+
+end;
+ 
+initialization
+  RegisterTests([TTestCSSParser,TTestCSSFilesParser]);
+end.
+

+ 710 - 0
packages/fcl-css/tests/tccssscanner.pp

@@ -0,0 +1,710 @@
+{
+    This file is part of the Free Pascal Run time library.
+    Copyright (c) 2022- by Michael Van Canneyt ([email protected])
+
+    This file contains the tests for the CSS Scanner
+
+    See the File COPYING.FPC, included in this distribution,
+    for details about the copyright.
+
+    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.
+
+ **********************************************************************}
+
+unit tcCSSScanner;
+
+{$mode objfpc}{$H+}
+
+interface
+
+uses
+  TypInfo, Classes, SysUtils, fpcunit, testutils, testregistry, fpcssscanner;
+
+type
+  TTestLineReader = Class(TTestCase)
+  Private
+    FData: TStringStream;
+    FReader : TStreamLineReader;
+  protected
+    Procedure CreateReader(AInput : String);
+    procedure TearDown; override;
+  published
+    Procedure TestEmpty;
+    Procedure TestReadLine;
+    Procedure TestReadLines13;
+    Procedure TestReadLines10;
+    Procedure TestReadLines1310;
+    procedure TestReadLinesEOF13;
+    procedure TestReadLinesEOF10;
+    procedure TestReadLinesEOF1310;
+    procedure TestReadEmptyLines101010;
+  end;
+
+  { TTestCSSScanner }
+
+  TTestCSSScanner= class(TTestCase)
+  Private
+    FPSeudoDisabled,
+    FNeedWhiteSpace : Boolean;
+    FStream : TStream;
+    FLineReader : TLineReader;
+    FScanner : TCSSScanner;
+    FErrorSource : String;
+    procedure AssertEquals(AMessage: String; AExpected, AActual : TCSSToken); overload;
+    procedure CheckToken(AToken: TCSSToken; ASource: String);
+    procedure CheckTokens(ASource: String; ATokens: array of TCSSToken);
+    procedure DoTestFloat(F: Double);
+    procedure DoTestFloat(F: Double; S: String);
+    procedure DoTestString(S: String);
+    procedure TestErrorSource;
+  protected
+    Function CreateScanner(AInput : String) : TCSSScanner;
+    procedure FreeScanner;
+    procedure SetUp; override;
+    procedure TearDown; override;
+    Property Scanner : TCSSScanner Read FScanner;
+  published
+    procedure TestEmpty;
+    Procedure TestEOF;
+    Procedure TestWhiteSpace;
+    Procedure TestComment1;
+    Procedure TestComment2;
+    Procedure TestComment3;
+    Procedure TestID1;
+    Procedure TestID2;
+    Procedure TestID3;
+    Procedure TestID4;
+    procedure TestPSEUDO;
+    procedure TestPSEUDO2;
+    procedure TestPSEUDOMinus;
+    procedure TestPSEUDO2Minus;
+    procedure TestPSEUDODisabled;
+    procedure TestUNICODERANGE;
+    procedure TestUNICODERANGE2;
+    procedure TestFUNCTION;
+    procedure TestPSEUDOFUNCTION;
+    procedure TestPSEUDOFUNCTION2;
+    Procedure TestSEMI;
+    Procedure TestLPAREN;
+    Procedure TestRPAREN;
+    Procedure TestLBRACE;
+    Procedure TestRBRACE;
+    Procedure TestLBRACKET;
+    Procedure TestRBRACKET;
+    Procedure TestSTR;
+    Procedure TestStrEscapeUnicode;
+    procedure TestStrEscapeUnicode2;
+    Procedure TestCOMMA;
+    Procedure TestEQUALS;
+    Procedure TestAND;
+    Procedure TestTILDE;
+    Procedure TestPLUS;
+    Procedure TestCOLON;
+    Procedure TestDOUBLECOLON;
+    Procedure TestDOT;
+    Procedure TestINT;
+    Procedure TestFLOAT;
+    Procedure TestDOUBLE;
+    Procedure TestHASH1;
+    Procedure TestHASH2;
+    Procedure TestDIV;
+    Procedure TestSTATEMENT;
+    Procedure TestGT;
+    Procedure TestLT;
+    Procedure TestPERCENTAGE;
+    Procedure TestMINUS;
+    Procedure TestIMPORTANT;
+    Procedure TestCLASSNAME;
+    Procedure TestSTAR;
+    Procedure TestURL;
+    Procedure TestURLAltQuote;
+    Procedure TestURL2;
+    Procedure TestBADURL;
+    Procedure TestBADEOF;
+    Procedure TestJUNK;
+    Procedure TestSQUARED;
+  end;
+
+implementation
+
+function TTestCSSScanner.CreateScanner(AInput: String): TCSSScanner;
+
+begin
+  FreeScanner;
+  FStream:=TStringStream.Create(AInput);
+  FLineReader:=TStreamLineReader.Create(Fstream);
+  FScanner:=TCSSScanner.Create(FLineReader);
+  FScanner.DisablePseudo:=FPSeudoDisabled;
+  Result:=FScanner;
+  if FNeedWhiteSpace then
+    FScanner.ReturnWhiteSpace:=True;
+end;
+
+procedure TTestCSSScanner.FreeScanner;
+begin
+  FreeAndNil(FScanner);
+  FreeAndNil(FLineReader);
+  FreeAndNil(FStream);
+end;
+
+procedure TTestCSSScanner.SetUp;
+begin
+  inherited SetUp;
+  FNeedWhiteSpace:=False;
+  FPSeudoDisabled:=False;
+end;
+
+procedure TTestCSSScanner.TearDown;
+begin
+  FreeScanner;
+  Inherited;
+end;
+
+procedure TTestCSSScanner.DoTestFloat(F : Double);
+
+Var
+  S : String;
+
+begin
+  Str(F,S);
+  DoTestFloat(F,S);
+end;
+
+procedure TTestCSSScanner.DoTestFloat(F : Double; S : String);
+
+Var
+  J : TCSSToken;
+  C : Double;
+  I : integer;
+  V : String;
+
+begin
+  CreateScanner(S);
+  try
+    J:=FScanner.FetchToken;
+    AssertEquals(S+' is a number',ctkFloat,J);
+    V:=FScanner.CurTokenString;
+    If (Copy(V,1,2)='0x') then
+      begin
+      Flush(output);
+      V:='$'+Copy(V,3,Length(V)-2);
+      C:=StrToInt(V);
+      end
+    else
+      begin
+      Val(V,C,I);
+      If (I<>0) then
+        Fail(FScanner.CurTokenString+' does not contain a float value');
+      end;
+    AssertEquals('Parsed float equals original float',F,C);
+  finally
+    FreeScanner;
+  end;
+end;
+
+procedure TTestCSSScanner.DoTestString(S: String);
+
+Var
+  J : TCSSToken;
+begin
+  CreateScanner(S);
+  try
+    J:=FScanner.FetchToken;
+    AssertEquals(S+' is a string',ctkSTRING,J);
+    If (Length(S)>0) and (S[1] in ['"','''','`']) then
+      S:=Copy(S,2,Length(S)-2);
+    AssertEquals('Correct string is returned',S,FScanner.CurTokenString);
+  finally
+    FreeScanner;
+  end;
+end;
+
+procedure TTestCSSScanner.TestErrorSource;
+
+begin
+  CreateScanner(FErrorSource);
+  try
+    While (FScanner.FetchToken<>ctkEOF) do ;
+  finally
+    FreeScanner;
+  end;
+end;
+
+procedure TTestCSSScanner.TestEmpty;
+
+Var
+  J : TCSSToken;
+
+begin
+  CreateScanner('');
+  J:=Scanner.FetchToken;
+  If (J<>ctkEOF) then
+    Fail('Empty returns EOF');
+end;
+
+procedure TTestCSSScanner.TestEOF;
+
+Var
+  C : TCSSToken;
+
+begin
+  CreateScanner('');
+  C:=Scanner.FetchToken;
+  If (C<>ctkEOF) then
+    Fail('Empty returns EOF');
+end;
+
+procedure TTestCSSScanner.TestWhiteSpace;
+Var
+  C : TCSSToken;
+
+begin
+  FNeedWhiteSpace:=True;
+  CreateScanner(' ');
+  C:=Scanner.FetchToken;
+  If (C<>ctkWHITESPACE) then
+    Fail('Empty returns EOF');
+end;
+
+procedure TTestCSSScanner.TestComment1;
+begin
+  CreateScanner('/* some comment string */');
+  AssertEquals('Comment line is skipped',ctkEOF,FScanner.FetchToken);
+end;
+
+procedure TTestCSSScanner.TestComment2;
+begin
+  CreateScanner('/* some comment string */');
+  FScanner.ReturnComments:=True;
+  AssertEquals('Comment line is returned',ctkComment,FScanner.FetchToken);
+  AssertEquals('Comment contents is returned',' some comment string ',FScanner.CurTokenString);
+end;
+
+procedure TTestCSSScanner.TestComment3;
+
+begin
+  CreateScanner('/* some multiline comment '#10' string */');
+  FScanner.ReturnComments:=True;
+  AssertEquals('Comment line is returned',ctkComment,FScanner.FetchToken);
+  AssertEquals('Comment contents is returned',' some multiline comment '#10' string ',FScanner.CurTokenString);
+end;
+
+procedure TTestCSSScanner.TestID1;
+begin
+  CheckToken(ctkIDENTIFIER,'anid');
+end;
+
+procedure TTestCSSScanner.TestID2;
+begin
+  CheckToken(ctkIDENTIFIER,'_anid');
+end;
+
+procedure TTestCSSScanner.TestID3;
+begin
+  CheckToken(ctkIDENTIFIER,'-anid');
+end;
+
+procedure TTestCSSScanner.TestID4;
+begin
+  CheckToken(ctkIDENTIFIER,'--anid');
+end;
+
+
+procedure TTestCSSScanner.TestSEMI;
+begin
+  CheckToken(ctkSEMICOLON,';');
+end;
+
+procedure TTestCSSScanner.TestLPAREN;
+begin
+  CheckToken(ctkLPARENTHESIS,'(');
+end;
+
+procedure TTestCSSScanner.TestRPAREN;
+begin
+  CheckToken(ctkRPARENTHESIS,')');
+end;
+
+procedure TTestCSSScanner.TestLBRACE;
+begin
+  CheckToken(ctkLBRACE,'{');
+end;
+
+procedure TTestCSSScanner.TestRBRACE;
+begin
+  CheckToken(ctkRBRACE,'}');
+end;
+
+procedure TTestCSSScanner.TestLBRACKET;
+begin
+  CheckToken(ctkLBRACKET,'[');
+end;
+
+procedure TTestCSSScanner.TestRBRACKET;
+begin
+  CheckToken(ctkRBRACKET,']');
+end;
+
+procedure TTestCSSScanner.TestSTR;
+begin
+  CheckToken(ctkSTRING,'"abc"');
+end;
+
+procedure TTestCSSScanner.TestStrEscapeUnicode;
+begin
+ CheckToken(ctkSTRING,'"\00a0\00a0\00a0\00a0"');
+ CheckToken(ctkSTRING,'"\2a"');
+end;
+
+procedure TTestCSSScanner.TestStrEscapeUnicode2;
+begin
+ CheckToken(ctkSTRING,'"\2a"');
+end;
+
+
+procedure TTestCSSScanner.TestCOMMA;
+begin
+  CheckToken(ctkCOMMA,',');
+end;
+
+procedure TTestCSSScanner.TestEQUALS;
+begin
+  CheckToken(ctkEQUALS,'=');
+end;
+
+procedure TTestCSSScanner.TestAND;
+begin
+  CheckToken(ctkAND,'&');
+end;
+
+procedure TTestCSSScanner.TestTILDE;
+begin
+  CheckToken(ctkTILDE,'~');
+end;
+
+procedure TTestCSSScanner.TestPLUS;
+begin
+  CheckToken(ctkPLUS,'+');
+end;
+
+procedure TTestCSSScanner.TestCOLON;
+begin
+  CheckToken(ctkCOLON,':');
+end;
+
+procedure TTestCSSScanner.TestDOUBLECOLON;
+begin
+  CheckToken(ctkDOUBLECOLON,'::');
+end;
+
+procedure TTestCSSScanner.TestDOT;
+begin
+  CheckToken(ctkDOT,'.');
+end;
+
+procedure TTestCSSScanner.TestINT;
+begin
+  CheckToken(ctkINTEGER,'123');
+end;
+
+procedure TTestCSSScanner.TestFLOAT;
+begin
+  DoTestFloat(123.3,'123.3');
+end;
+
+procedure TTestCSSScanner.TestDOUBLE;
+begin
+  DoTestFloat(123.3,'123.3');
+end;
+
+procedure TTestCSSScanner.TestHASH1;
+begin
+  CheckToken(ctkHASH,'#1231');
+end;
+
+procedure TTestCSSScanner.TestHASH2;
+begin
+  CheckToken(ctkHASH,'#AFAFAF');
+end;
+
+procedure TTestCSSScanner.TestDIV;
+begin
+  CheckToken(ctkDIV,'/');
+end;
+
+procedure TTestCSSScanner.TestSTATEMENT;
+begin
+  CheckToken(ctkATKEYWORD,'@media');
+end;
+
+procedure TTestCSSScanner.TestGT;
+begin
+  CheckToken(ctkGT,'>');
+end;
+
+procedure TTestCSSScanner.TestLT;
+begin
+  CheckToken(ctkLT,'<');
+end;
+
+procedure TTestCSSScanner.TestPERCENTAGE;
+begin
+  CheckToken(ctkPERCENTAGE,'%');
+end;
+
+procedure TTestCSSScanner.TestMINUS;
+begin
+  CheckToken(ctkMinus,'-');
+end;
+
+procedure TTestCSSScanner.TestIMPORTANT;
+begin
+  CheckToken(ctkIMPORTANT,'!important');
+end;
+
+procedure TTestCSSScanner.TestCLASSNAME;
+begin
+  CheckToken(ctkCLASSNAME,'.classname');
+end;
+
+procedure TTestCSSScanner.TestSTAR;
+begin
+  CheckToken(ctkSTAR,'*');
+end;
+
+procedure TTestCSSScanner.TestURL;
+begin
+  CheckToken(ctkURL,'url("abc")');
+end;
+
+procedure TTestCSSScanner.TestURLAltQuote;
+begin
+  CheckToken(ctkURL,'url(''abc'')');
+end;
+
+procedure TTestCSSScanner.TestURL2;
+begin
+  CheckToken(ctkURL,'url(abc)');
+end;
+
+procedure TTestCSSScanner.TestBADURL;
+begin
+  CheckToken(ctkBADURL,'url(de f)');
+end;
+
+procedure TTestCSSScanner.TestBADEOF;
+begin
+  CheckToken(ctkEOF,'url(def');
+end;
+
+
+procedure TTestCSSScanner.TestPSEUDO;
+begin
+  CheckToken(ctkPSEUDO,':name');
+end;
+
+procedure TTestCSSScanner.TestPSEUDO2;
+begin
+  CheckToken(ctkPSEUDO,'::name');
+end;
+
+procedure TTestCSSScanner.TestPSEUDOMinus;
+begin
+  CheckToken(ctkPSEUDO,':-name');
+end;
+
+procedure TTestCSSScanner.TestPSEUDO2Minus;
+begin
+  CheckToken(ctkPSEUDO,'::-name');
+end;
+
+procedure TTestCSSScanner.TestPSEUDODisabled;
+begin
+  FPseudoDisabled:=True;
+  CheckTokens(':me',[ctkColon,ctkIDENTIFIER]);
+end;
+
+procedure TTestCSSScanner.TestUNICODERANGE;
+begin
+  CheckToken(ctkUNICODERANGE,'U+124');
+end;
+
+procedure TTestCSSScanner.TestUNICODERANGE2;
+begin
+  CheckToken(ctkUNICODERANGE,'U+124-128');
+
+end;
+
+procedure TTestCSSScanner.TestFUNCTION;
+begin
+  CheckToken(ctkFUNCTION,'name(');
+end;
+
+procedure TTestCSSScanner.TestPSEUDOFUNCTION;
+begin
+  CheckToken(ctkPSEUDOFUNCTION,':name(');
+end;
+
+procedure TTestCSSScanner.TestPSEUDOFUNCTION2;
+begin
+  CheckToken(ctkPSEUDOFUNCTION,'::name(');
+end;
+
+
+
+procedure TTestCSSScanner.TestJUNK;
+begin
+  FErrorSource:='?';
+  AssertException('Exception',ECSSScanner, @TestErrorSource);
+end;
+
+procedure TTestCSSScanner.TestSQUARED;
+begin
+  CheckToken(ctkSQUARED,'^');
+end;
+
+procedure TTestCSSScanner.AssertEquals(AMessage : String; AExpected, AActual: TCSSToken);
+
+Var
+  S,EN1,EN2 : String;
+
+begin
+  If (AActual<>AExpected) then
+    begin
+    EN1:=GetEnumName(TypeINfo(TCSSToken),Ord(AExpected));
+    EN2:=GetEnumName(TypeINfo(TCSSToken),Ord(AActual));
+    S:=Format('%s : %s <> %s',[AMessage,EN1,EN2]);
+    Fail(S);
+    end;
+end;
+
+procedure TTestCSSScanner.CheckToken(AToken: TCSSToken; ASource: String);
+
+Var
+  J : TCSSToken;
+  EN2 : String;
+
+begin
+  CreateScanner(ASource);
+  J:=Scanner.FetchToken;
+  EN2:=GetEnumName(TypeINfo(TCSSToken),Ord(AToken));
+  AssertEquals(Format('Source %s should result in %s.',[ASource,EN2]),AToken,J);
+end;
+
+procedure TTestCSSScanner.CheckTokens(ASource: String;
+  ATokens: array of TCSSToken);
+
+Var
+  I : Integer;
+  J : TCSSToken;
+  S : String;
+
+begin
+  CreateScanner(ASource);
+  For I:=Low(ATokens) to High(ATokens) do
+    begin
+    J:=FScanner.FetchToken;
+    S:=GetEnumName(TypeINfo(TCSSToken),Ord(ATokens[i]));
+    S:=Format('Source "%s", token %d (%s): expected %s',[ASource,I,FScanner.CurTokenString,S]);
+    AssertEquals(S,ATokens[i],J);
+    end;
+end;
+
+
+
+{ TTestLineReader }
+
+procedure TTestLineReader.CreateReader(AInput: String);
+begin
+  FData:=TStringStream.Create(AInput);
+  FReader:=TStreamLineReader.Create(FData);
+end;
+
+
+procedure TTestLineReader.TearDown;
+begin
+  FreeAndNil(FReader);
+  FreeAndNil(FData);
+end;
+
+procedure TTestLineReader.TestEmpty;
+begin
+  CreateReader('');
+  AssertEquals('Empty reader returns EOF',True,FReader.IsEOF);
+  AssertEquals('Empty reader returns empty string','',FReader.ReadLine);
+end;
+
+procedure TTestLineReader.TestReadLine;
+begin
+  CreateReader('Something');
+  AssertEquals('Reader with 1 line returns 1 line','Something',FReader.ReadLine);
+  AssertEquals('EOF true after reading line',True,FReader.IsEOF);
+end;
+
+procedure TTestLineReader.TestReadLines13;
+begin
+  CreateReader('Something'#13'else');
+  AssertEquals('Reader with 2 lines returns 1st line','Something',FReader.ReadLine);
+  AssertEquals('Reader with 2 lines returns 2nd line','else',FReader.ReadLine);
+  AssertEquals('EOF true after reading lines',True,FReader.IsEOF);
+end;
+
+procedure TTestLineReader.TestReadLines10;
+begin
+  CreateReader('Something'#10'else');
+  AssertEquals('Reader with 2 lines returns 1st line','Something',FReader.ReadLine);
+  AssertEquals('Reader with 2 lines returns 2nd line','else',FReader.ReadLine);
+  AssertEquals('EOF true after reading lines',True,FReader.IsEOF);
+end;
+
+procedure TTestLineReader.TestReadLines1310;
+begin
+  CreateReader('Something'#13#10'else');
+  AssertEquals('Reader with 2 lines returns 1st line','Something',FReader.ReadLine);
+  AssertEquals('Reader with 2 lines returns 2nd line','else',FReader.ReadLine);
+  AssertEquals('EOF true after reading lines',True,FReader.IsEOF);
+end;
+
+procedure TTestLineReader.TestReadLinesEOF13;
+begin
+  CreateReader('Something'#13);
+  AssertEquals('Reader with 2 lines + CR returns 1st line','Something',FReader.ReadLine);
+  AssertEquals('Reader with 1 lines + CR returns empty 2nd line','',FReader.ReadLine);
+  AssertEquals('EOF true after reading lines',True,FReader.IsEOF);
+end;
+
+procedure TTestLineReader.TestReadLinesEOF10;
+begin
+  CreateReader('Something'#10);
+  AssertEquals('Reader with 2 lines + LF returns 1st line','Something',FReader.ReadLine);
+  AssertEquals('Reader with 1 lines + LF returns empty 2nd line','',FReader.ReadLine);
+  AssertEquals('EOF true after reading lines',True,FReader.IsEOF);
+end;
+
+procedure TTestLineReader.TestReadLinesEOF1310;
+begin
+  CreateReader('Something'#13#10);
+  AssertEquals('Reader with 2 lines + CRLF returns 1st line','Something',FReader.ReadLine);
+  AssertEquals('Reader with 1 lines + CRLF returns empty 2nd line','',FReader.ReadLine);
+  AssertEquals('EOF true after reading lines',True,FReader.IsEOF);
+end;
+
+procedure TTestLineReader.TestReadEmptyLines101010;
+
+begin
+  CreateReader('Something'#10#10#10);
+  AssertEquals('Reader with 1 line + LFLFLF returns 1st line','Something',FReader.ReadLine);
+  AssertEquals('EOF false after reading line 1',False,FReader.IsEOF);
+  AssertEquals('Reader with 1 line + LFLFLF returns empty 2nd line','',FReader.ReadLine);
+  AssertEquals('EOF false after reading line 2',False,FReader.IsEOF);
+  AssertEquals('Reader with 1 line + LFLFLF returns empty 3nd line','',FReader.ReadLine);
+  AssertEquals('EOF true after reading lines',True,FReader.IsEOF);
+end;
+
+initialization
+
+  RegisterTests([TTestLineReader,TTestCSSScanner]);
+end.
+

+ 792 - 0
packages/fcl-css/tests/tccsstree.pp

@@ -0,0 +1,792 @@
+unit tcCSSTree;
+
+{$mode ObjFPC}{$H+}
+
+interface
+
+uses
+  TypInfo, Classes, SysUtils, fpcunit, testregistry, fpCSSTree;
+
+type
+
+  { TBaseCSSTreeTest }
+  TAppendMode = (amReplace,amNone,amChild,amSelector,amKey);
+
+  TBaseCSSTreeTest = Class(TTestCase)
+  private
+    FElement: TCSSElement;
+  Protected
+    Procedure SetUp; override;
+    Procedure TearDown; override;
+    procedure AssertEquals(AMessage: String; AExpected, AActual: TCSSType);  overload;
+    Function CreateElement(aClass : TCSSElementClass; aAppend : TAppendMode = amReplace) : TCSSElement;
+    Function CreateIdentifier(const avalue : string; aAppend : TAppendMode = amNone): TCSSIdentifierElement;
+    Function CreateDeclaration(const aKey,avalue : string; aAppend : TAppendMode = amNone): TCSSDeclarationElement;
+    Function CreateBinaryOperation(aOp : TCSSBinaryOperation;const aLeft,aRight : string; aAppend : TAppendMode = amNone): TCSSBinaryElement;
+    Function CreateUnaryOperation(aOp : TCSSUnaryOperation;const aRight : string; aAppend : TAppendMode = amNone): TCSSUnaryElement;
+    Property Element : TCSSElement Read FElement Write FElement;
+  end;
+
+  { TCSSTreeTypeTest }
+
+  TCSSTreeTypeTest = Class(TBaseCSSTreeTest)
+  Published
+    Procedure TestINTEGER;
+    Procedure TestSTRING;
+    Procedure TestFLOAT;
+    Procedure TestIDENTIFIER;
+    Procedure TestCLASSNAME;
+    Procedure TestPSEUDOCLASS;
+    Procedure TestCOMPOUND;
+    Procedure TestRULE;
+    Procedure TestDECLARATION;
+    Procedure TestBINARYOP;
+    Procedure TestCALL;
+    Procedure TestUNARYOP;
+    Procedure TestARRAY;
+    Procedure TestURL;
+    Procedure TestUNICODERANGE;
+  end;
+
+  { TCSSTreeAsStringTest }
+
+  TCSSTreeAsStringTest = Class(TBaseCSSTreeTest)
+  Published
+    Procedure TestINTEGER;
+    Procedure TestSTRING;
+    Procedure TestFLOAT;
+    Procedure TestIDENTIFIER;
+    Procedure TestCLASSNAME;
+    Procedure TestPSEUDOCLASS;
+    Procedure TestCOMPOUND;
+    Procedure TestRULE;
+    Procedure TestRULE2Declarations;
+    Procedure TestRULESelector;
+    Procedure TestRULE2Selectors;
+    Procedure TestRULE2SelectorCombined;
+    Procedure TestAtRULE;
+    Procedure TestDECLARATION;
+    Procedure TestDECLARATIONList;
+    Procedure TestBINARYOP;
+    Procedure TestCALL;
+    Procedure TestUNARYOP;
+    Procedure TestARRAY;
+    Procedure TestURL;
+    Procedure TestUNICODERANGE;
+  end;
+
+  { TEnumVisitor }
+
+  TEnumVisitor = Class (TCSSTreeVisitor)
+  Private
+    FList: TFPList;
+  Public
+    Constructor Create(aList : TFPList);
+    procedure Visit(obj: TCSSElement); override;
+  end;
+
+  { TCSSTreeVisitorTest }
+
+  TCSSTreeVisitorTest = Class(TBaseCSSTreeTest)
+  Private
+    FList: TFPList;
+    FVisitor: TCSSTreeVisitor;
+  Public
+    Procedure Setup; override;
+    Procedure TearDown; override;
+    Procedure CheckElement(aIndex : Integer; aElement : TCSSElement);
+    Procedure CheckCount(aCount : Integer);
+    Property List : TFPList Read FList;
+    Property Visitor : TCSSTreeVisitor Read FVisitor;
+  Published
+    Procedure TestElement;
+    Procedure TestINTEGER;
+    Procedure TestSTRING;
+    Procedure TestFLOAT;
+    Procedure TestIDENTIFIER;
+    Procedure TestCLASSNAME;
+    Procedure TestPSEUDOCLASS;
+    Procedure TestCOMPOUND;
+    Procedure TestRULE;
+    Procedure TestRULE2Declarations;
+    Procedure TestRULESelector;
+    Procedure TestRULE2Selectors;
+    Procedure TestRULE2SelectorCombined;
+    Procedure TestAtRULE;
+    Procedure TestDECLARATION;
+    Procedure TestDECLARATIONList;
+    Procedure TestBINARYOP;
+    Procedure TestCALL;
+    Procedure TestUNARYOP;
+    Procedure TestARRAY;
+    Procedure TestURL;
+    Procedure TestUNICODERANGE;
+  end;
+
+
+implementation
+
+{ TCSSTreeVisitorTest }
+
+procedure TCSSTreeVisitorTest.Setup;
+begin
+  inherited Setup;
+  FList:=TFPList.Create;
+  FVisitor:=TEnumVisitor.Create(FList);
+end;
+
+procedure TCSSTreeVisitorTest.TearDown;
+begin
+  FreeAndNil(FVisitor);
+  FreeAndNil(FList);
+  inherited TearDown;
+end;
+
+procedure TCSSTreeVisitorTest.CheckElement(aIndex: Integer; aElement: TCSSElement);
+begin
+  AssertTrue(Format('Index in range: %d in [0..%d[',[aIndex,FList.Count]),(aIndex>=0) and (aIndex<FList.Count));
+  AssertSame(Format('Element %d is correct',[aIndex]),aElement,TObject(FList[aindex]));
+end;
+
+procedure TCSSTreeVisitorTest.CheckCount(aCount: Integer);
+begin
+  AssertEquals('Count is correct',aCount,FList.Count);
+end;
+
+procedure TCSSTreeVisitorTest.TestElement;
+
+
+begin
+  CreateElement(TCSSElement);
+  Element.Iterate(Visitor);
+  CheckCount(1);
+  CheckElement(0,Element);
+end;
+
+procedure TCSSTreeVisitorTest.TestINTEGER;
+begin
+  CreateElement(TCSSIntegerElement);
+  Element.Iterate(Visitor);
+  CheckCount(1);
+  CheckElement(0,Element);
+end;
+
+procedure TCSSTreeVisitorTest.TestSTRING;
+begin
+  CreateElement(TCSSStringElement);
+  Element.Iterate(Visitor);
+  CheckCount(1);
+  CheckElement(0,Element);
+end;
+
+procedure TCSSTreeVisitorTest.TestFLOAT;
+begin
+  CreateElement(TCSSFloatElement);
+  Element.Iterate(Visitor);
+  CheckCount(1);
+  CheckElement(0,Element);
+end;
+
+procedure TCSSTreeVisitorTest.TestIDENTIFIER;
+begin
+  CreateElement(TCSSIdentifierElement);
+  Element.Iterate(Visitor);
+  CheckCount(1);
+  CheckElement(0,Element);
+end;
+
+procedure TCSSTreeVisitorTest.TestCLASSNAME;
+begin
+  CreateElement(TCSSClassNameElement);
+  Element.Iterate(Visitor);
+  CheckCount(1);
+  CheckElement(0,Element);
+end;
+
+procedure TCSSTreeVisitorTest.TestPSEUDOCLASS;
+begin
+  CreateElement(TCSSPseudoClassElement);
+  Element.Iterate(Visitor);
+  CheckCount(1);
+  CheckElement(0,Element);
+end;
+
+procedure TCSSTreeVisitorTest.TestCOMPOUND;
+begin
+  CreateElement(TCSSCompoundElement);
+  Element.Iterate(Visitor);
+  CheckCount(1);
+  CheckElement(0,Element);
+end;
+
+procedure TCSSTreeVisitorTest.TestRULE;
+begin
+  CreateElement(TCSSRuleElement);
+  Element.Iterate(Visitor);
+  CheckCount(1);
+  CheckElement(0,Element);
+end;
+
+procedure TCSSTreeVisitorTest.TestRULE2Declarations;
+begin
+
+end;
+
+procedure TCSSTreeVisitorTest.TestRULESelector;
+begin
+
+end;
+
+procedure TCSSTreeVisitorTest.TestRULE2Selectors;
+begin
+
+end;
+
+procedure TCSSTreeVisitorTest.TestRULE2SelectorCombined;
+begin
+
+end;
+
+procedure TCSSTreeVisitorTest.TestAtRULE;
+begin
+  CreateElement(TCSSAtRuleElement);
+  Element.Iterate(Visitor);
+  CheckCount(1);
+  CheckElement(0,Element);
+end;
+
+procedure TCSSTreeVisitorTest.TestDECLARATION;
+
+Var
+  Decl: TCSSDeclarationElement;
+
+begin
+  Decl:=CreateDeclaration('a','b',amReplace);
+  Element.Iterate(Visitor);
+  CheckCount(3);
+  CheckElement(0,Element);
+  CheckElement(1,Decl.Keys[0]);
+  CheckElement(2,Decl.Children[0]);
+end;
+
+procedure TCSSTreeVisitorTest.TestDECLARATIONList;
+Var
+  Decl: TCSSDeclarationElement;
+
+begin
+  Decl:=CreateDeclaration('a','b',amReplace);
+  CreateIdentifier('c',amChild);
+  Element.Iterate(Visitor);
+  CheckCount(4);
+  CheckElement(0,Element);
+  CheckElement(1,Decl.Keys[0]);
+  CheckElement(2,Decl.Children[0]);
+  CheckElement(3,Decl.Children[1]);
+end;
+
+procedure TCSSTreeVisitorTest.TestBINARYOP;
+
+Var
+  Bin : TCSSBinaryElement;
+
+begin
+  Bin:=CreateBinaryOperation(boAnd,'a','b',amReplace);
+  Element.Iterate(Visitor);
+  CheckCount(3);
+  CheckElement(0,Element);
+  CheckElement(1,Bin.Right);
+  CheckElement(2,Bin.Left);
+end;
+
+procedure TCSSTreeVisitorTest.TestCALL;
+
+Var
+  aEl : TCSSElement;
+
+begin
+  CreateElement(TCSSCallElement);
+  aEl:=CreateIdentifier('a',amChild);
+  Element.Iterate(Visitor);
+  CheckCount(2);
+  CheckElement(0,Element);
+  CheckElement(1,aEl);
+end;
+
+procedure TCSSTreeVisitorTest.TestUNARYOP;
+
+begin
+  CreateUnaryOperation(uoDoubleColon,'a',amReplace);
+  Element.Iterate(Visitor);
+  CheckCount(2);
+  CheckElement(0,Element);
+  CheckElement(1,TCSSUnaryElement(Element).Right);
+end;
+
+procedure TCSSTreeVisitorTest.TestARRAY;
+begin
+  CreateElement(TCSSArrayElement);
+  CreateIdentifier('a',amChild);
+  CreateIdentifier('b',amChild);
+  Element.Iterate(Visitor);
+  CheckCount(3);
+  CheckElement(0,Element);
+  CheckElement(1,TCSSArrayElement(Element).Children[0]);
+  CheckElement(2,TCSSArrayElement(Element).Children[1]);
+end;
+
+procedure TCSSTreeVisitorTest.TestURL;
+begin
+  CreateElement(TCSSURLElement);
+  Element.Iterate(Visitor);
+  CheckCount(1);
+  CheckElement(0,Element);
+end;
+
+procedure TCSSTreeVisitorTest.TestUNICODERANGE;
+begin
+  CreateElement(TCSSUnicodeRangeElement);
+  Element.Iterate(Visitor);
+  CheckCount(1);
+  CheckElement(0,Element);
+end;
+
+{ TEnumVisitor }
+
+constructor TEnumVisitor.Create(aList: TFPList);
+begin
+  FList:=AList;
+end;
+
+procedure TEnumVisitor.Visit(obj: TCSSElement);
+begin
+  FList.Add(obj);
+end;
+
+{ TCSSTreeAsStringTest }
+
+procedure TCSSTreeAsStringTest.TestINTEGER;
+begin
+  TCSSIntegerElement(CreateElement(TCSSIntegerElement)).Value:=123;
+  AssertEquals('Value','123',Element.AsString);
+end;
+
+procedure TCSSTreeAsStringTest.TestSTRING;
+begin
+  TCSSStringElement(CreateElement(TCSSStringElement)).Value:='abc';
+  AssertEquals('Value','"abc"',Element.AsString);
+end;
+
+procedure TCSSTreeAsStringTest.TestFLOAT;
+begin
+  TCSSFloatElement(CreateElement(TCSSFloatElement)).Value:=1.23;
+  AssertEquals('Value','1.23',Element.AsString);
+end;
+
+procedure TCSSTreeAsStringTest.TestIDENTIFIER;
+begin
+  TCSSIdentifierElement(CreateElement(TCSSIdentifierElement)).Value:='abc';
+  AssertEquals('Value','abc',Element.AsString);
+end;
+
+procedure TCSSTreeAsStringTest.TestCLASSNAME;
+begin
+  TCSSClassNameElement(CreateElement(TCSSClassNameElement)).Value:='.abc';
+  AssertEquals('Value','.abc',Element.AsString);
+end;
+
+procedure TCSSTreeAsStringTest.TestPSEUDOCLASS;
+begin
+  TCSSClassNameElement(CreateElement(TCSSClassNameElement)).Value:=':abc';
+  AssertEquals('Value',':abc',Element.AsString);
+  TCSSClassNameElement(CreateElement(TCSSClassNameElement)).Value:='::abc';
+  AssertEquals('Value','::abc',Element.AsString);
+end;
+
+procedure TCSSTreeAsStringTest.TestCOMPOUND;
+
+Var
+  aRule : TCSSRuleElement;
+
+begin
+  CreateElement(TCSSCompoundElement);
+  aRule:=TCSSRuleElement(CreateElement(TCSSRuleElement,amChild));
+  aRule.AddChild(CreateDeclaration('a','b',amNone));
+  aRule:=TCSSRuleElement(CreateElement(TCSSRuleElement,amChild));
+  aRule.AddChild(CreateDeclaration('c','d',amNone));
+  aRule.AddSelector(CreateIdentifier('p',amNone));
+  AssertEquals('Value','{ a : b; }p { c : d; }',Element.AsString);
+  AssertEquals('Value','{'+sLineBreak+'  a : b;'+sLineBreak+'}'+sLineBReak+'p {'+sLineBReak+'  c : d;'+sLineBreak+'}',Element.AsFormattedString);
+end;
+
+procedure TCSSTreeAsStringTest.TestRULE;
+
+begin
+  CreateElement(TCSSRuleElement);
+  CreateDeclaration('a','b',amChild);
+  AssertEquals('Value','{ a : b; }',Element.AsString);
+  AssertEquals('Value','{'+sLineBreak+'  a : b;'+sLineBreak+'}',Element.AsFormattedString);
+end;
+
+procedure TCSSTreeAsStringTest.TestRULE2Declarations;
+begin
+  CreateElement(TCSSRuleElement);
+  CreateDeclaration('a','b',amChild);
+  CreateDeclaration('c','d',amChild);
+  AssertEquals('Value','{ a : b; c : d; }',Element.AsString);
+  AssertEquals('Value','{'+sLineBreak+'  a : b;'+sLineBreak+'  c : d;'+sLineBreak+'}',Element.AsFormattedString);
+end;
+
+procedure TCSSTreeAsStringTest.TestRULESelector;
+
+Var
+  aIdent : TCSSIdentifierElement;
+
+begin
+  CreateElement(TCSSRuleElement);
+  CreateDeclaration('a','b',amChild);
+  aIdent:=CreateIdentifier('c',amSelector);
+  AssertEquals('Value','c { a : b; }',Element.AsString);
+  AssertEquals('Value','c {'+sLineBreak+'  a : b;'+sLineBreak+'}',Element.AsFormattedString);
+end;
+
+procedure TCSSTreeAsStringTest.TestRULE2Selectors;
+Var
+  aIdent : TCSSIdentifierElement;
+
+begin
+  CreateElement(TCSSRuleElement);
+  CreateDeclaration('a','b',amChild);
+  aIdent:=CreateIdentifier('c',amSelector);
+  aIdent:=CreateIdentifier('d',amSelector);
+  AssertEquals('Value','c, d { a : b; }',Element.AsString);
+  AssertEquals('Value','c,'+sLineBreak+'d {'+sLineBreak+'  a : b;'+sLineBreak+'}',Element.AsFormattedString);
+end;
+
+procedure TCSSTreeAsStringTest.TestRULE2SelectorCombined;
+Var
+  aList : TCSSListElement;
+  aIdent : TCSSIdentifierElement;
+
+begin
+  CreateElement(TCSSRuleElement);
+  CreateDeclaration('a','b',amChild);
+  aList:=TCSSListElement(CreateElement(TCSSListElement,amSelector));
+  aIdent:=CreateIdentifier('c',amNone);
+  aList.AddChild(aIdent);
+  aIdent:=CreateIdentifier('d',amNone);
+  aList.AddChild(aIdent);
+  aIdent:=CreateIdentifier('e',amSelector);
+  AssertEquals('Value','c d, e { a : b; }',Element.AsString);
+  AssertEquals('Value','c d,'+sLineBreak+'e {'+sLineBreak+'  a : b;'+sLineBreak+'}',Element.AsFormattedString);
+
+end;
+
+procedure TCSSTreeAsStringTest.TestAtRULE;
+Var
+  aATRule : TCSSAtRuleElement;
+  aURL : TCSSURLElement;
+
+begin
+  aATRule:=TCSSAtRuleElement(CreateElement(TCSSAtRuleElement));
+  aATRule.atKeyWord:='@import';
+  aURL:=TCSSURLElement(CreateElement(TCSSURLElement,amSelector));
+  aURL.Value:='url("me.css")';
+  AssertEquals('Value','@import url("me.css");',Element.AsFormattedString);
+end;
+
+procedure TCSSTreeAsStringTest.TestDECLARATION;
+begin
+  CreateDeclaration('a','b',amReplace);
+  AssertEquals('Value','a : b',Element.AsString)
+end;
+
+procedure TCSSTreeAsStringTest.TestDECLARATIONList;
+
+Var
+  aList: TCSSListElement;
+
+begin
+  CreateElement(TCSSDeclarationElement);
+  CreateIdentifier('a',amKey);
+  aList:=TCSSListElement(CreateElement(TCSSListElement,amChild));
+  aList.AddChild(CreateIdentifier('b',amNone));
+  aList.AddChild(CreateIdentifier('c',amNone));
+  CreateIdentifier('d',amChild);
+  AssertEquals('Value','a : b c, d',Element.AsString)
+end;
+
+procedure TCSSTreeAsStringTest.TestBINARYOP;
+
+Const
+  MyBinaryOperators : Array[TCSSBinaryOperation] of string =
+        ('=','+','-','and','<','>','/','*','~',':','::','^');
+
+Var
+  Op : TCSSBinaryOperation;
+  Sop : String;
+
+begin
+  For Op in TCSSBinaryOperation do
+    begin
+    CreateBinaryOperation(Op,'a','b',amReplace);
+    Sop:=MyBinaryOperators[Op];
+    if Not (Op in [boColon,boDoubleColon]) then
+      Sop:=' '+Sop+' ';
+    AssertEquals('Value '+Sop,'a'+sop+'b',Element.AsString)
+    end;
+end;
+
+
+procedure TCSSTreeAsStringTest.TestCALL;
+
+Var
+  aCall : TCSSCallElement;
+
+begin
+  aCall:=TCSSCallElement(CreateElement(TCSSCallElement));
+  aCall.Name:='me';
+  CreateIdentifier('a',amChild);
+  AssertEquals('Value','me(a)',Element.AsString);
+end;
+
+procedure TCSSTreeAsStringTest.TestUNARYOP;
+
+Const
+  MyUnaryOperators : Array[TCSSUnaryOperation] of string =
+      ('::','-','+','/');
+Var
+  Op : TCSSUnaryOperation;
+  Sop : String;
+
+begin
+  For Op in TCSSUnaryOperation do
+    begin
+    CreateUnaryOperation(op,'a',amReplace);
+    Sop:=MyUnaryOperators[Op];
+    if Not (Op in [uoDoubleColon]) then
+      Sop:=Sop+' ';
+    AssertEquals('Value '+Sop,sop+'a',Element.AsString)
+    end;
+
+end;
+
+procedure TCSSTreeAsStringTest.TestARRAY;
+
+begin
+  CreateElement(TCSSArrayElement);
+  CreateIdentifier('a',amChild);
+  CreateIdentifier('b',amChild);
+  AssertEquals('Value','[a b]',Element.AsString);
+  AssertEquals('Value','[a b]',Element.AsFormattedString);
+end;
+
+procedure TCSSTreeAsStringTest.TestURL;
+
+Var
+  Url : TCSSURLElement;
+
+begin
+  Url:=TCSSURLElement(CreateElement(TCSSURLElement));
+  Url.Value:='url("a.png")';
+  AssertEquals('Value','url("a.png")',Element.AsString);
+end;
+
+procedure TCSSTreeAsStringTest.TestUNICODERANGE;
+Var
+  Url : TCSSUnicodeRangeElement;
+
+begin
+  Url:=TCSSUnicodeRangeElement(CreateElement(TCSSUnicodeRangeElement));
+  Url.Value:='U+3588-488';
+  AssertEquals('Value','U+3588-488',Element.AsString);
+end;
+
+{ TCSSTreeTypeTest }
+
+procedure TCSSTreeTypeTest.TestINTEGER;
+begin
+  AssertEquals('Type',csstINTEGER,CreateElement(TCSSIntegerElement).CSSType);
+end;
+
+procedure TCSSTreeTypeTest.TestSTRING;
+begin
+  AssertEquals('Type',csstString,CreateElement(TCSSStringElement).CSSType);
+end;
+
+procedure TCSSTreeTypeTest.TestFLOAT;
+begin
+  AssertEquals('Type',csstFloat,CreateElement(TCSSFloatElement).CSSType);
+end;
+
+
+procedure TCSSTreeTypeTest.TestIDENTIFIER;
+begin
+  AssertEquals('Type',csstINTEGER,CreateElement(TCSSIntegerElement).CSSType);
+end;
+
+procedure TCSSTreeTypeTest.TestCLASSNAME;
+begin
+  AssertEquals('Type',csstClassName,CreateElement(TCSSClassNameElement).CSSType);
+end;
+
+procedure TCSSTreeTypeTest.TestPSEUDOCLASS;
+begin
+  AssertEquals('Type',csstPseudoClass,CreateElement(TCSSPseudoClassElement).CSSType);
+end;
+
+procedure TCSSTreeTypeTest.TestCOMPOUND;
+begin
+  AssertEquals('Type',csstCompound,CreateElement(TCSSCompoundElement).CSSType);
+end;
+
+procedure TCSSTreeTypeTest.TestRULE;
+begin
+  AssertEquals('Type',csstRule,CreateElement(TCSSRuleElement).CSSType);
+end;
+
+procedure TCSSTreeTypeTest.TestDECLARATION;
+begin
+  AssertEquals('Type',csstDeclaration,CreateElement(TCSSDeclarationElement).CSSType);
+end;
+
+procedure TCSSTreeTypeTest.TestBINARYOP;
+begin
+  AssertEquals('Type',csstBinaryOp,CreateElement(TCSSBinaryElement).CSSType);
+end;
+
+
+procedure TCSSTreeTypeTest.TestCALL;
+begin
+  AssertEquals('Type',csstCall,CreateElement(TCSSCallElement).CSSType);
+end;
+
+
+procedure TCSSTreeTypeTest.TestUNARYOP;
+begin
+  AssertEquals('Type',csstUnaryOp,CreateElement(TCSSUnaryElement).CSSType);
+end;
+
+procedure TCSSTreeTypeTest.TestARRAY;
+begin
+  AssertEquals('Type',csstArray,CreateElement(TCSSArrayElement).CSSType);
+end;
+
+procedure TCSSTreeTypeTest.TestURL;
+begin
+  AssertEquals('Type',csstURL,CreateElement(TCSSURLElement).CSSType);
+end;
+
+procedure TCSSTreeTypeTest.TestUNICODERANGE;
+begin
+  AssertEquals('Type',csstUnicodeRange,CreateElement(TCSSUnicodeRangeElement).CSSType);
+end;
+
+{ TBaseCSSTreeTest }
+
+procedure TBaseCSSTreeTest.SetUp;
+begin
+  inherited SetUp;
+  FreeAndNil(Felement);
+end;
+
+procedure TBaseCSSTreeTest.TearDown;
+begin
+  FreeAndNil(Felement);
+  inherited TearDown;
+end;
+
+procedure TBaseCSSTreeTest.AssertEquals(AMessage : String; AExpected, AActual: TCSSType);
+
+Var
+  S,EN1,EN2 : String;
+
+begin
+  If (AActual<>AExpected) then
+    begin
+    EN1:=GetEnumName(TypeINfo(TCSSType),Ord(AExpected));
+    EN2:=GetEnumName(TypeINfo(TCSSType),Ord(AActual));
+    S:=Format('%s : %s <> %s',[AMessage,EN1,EN2]);
+    Fail(S);
+    end;
+end;
+
+function TBaseCSSTreeTest.CreateElement(aClass: TCSSElementClass; aAppend : TAppendMode = amReplace): TCSSElement;
+begin
+  Result:=aClass.Create(TestName+'.css',1,1);
+  Case aAppend of
+    amNone : ;
+    amReplace :
+      begin
+      FreeAndNil(FElement);
+      FElement:=Result;
+      end;
+    amChild:
+      begin
+      if FElement is TCSSChildrenElement then
+        TCSSChildrenElement(FElement).AddChild(Result);
+      end;
+    amSelector:
+      begin
+      if FElement is TCSSRuleElement then
+        TCSSRuleElement(FElement).AddSelector(Result);
+      end;
+    amKey:
+      begin
+      if FElement is TCSSDeclarationElement then
+        TCSSDeclarationElement(FElement).AddKey(Result);
+      end;
+  end;
+end;
+
+function TBaseCSSTreeTest.CreateIdentifier(const avalue: string;
+  aAppend: TAppendMode): TCSSIdentifierElement;
+begin
+  Result:=TCSSIdentifierElement(CreateElement(TCSSIdentifierElement,aAppend));
+  Result.Value:=aValue;
+end;
+
+function TBaseCSSTreeTest.CreateDeclaration(const aKey, avalue: string; aAppend : TAppendMode = amNone): TCSSDeclarationElement;
+
+var
+  aIdent : TCSSIdentifierElement;
+
+begin
+  Result:=TCSSDeclarationElement(CreateElement(TCSSDeclarationElement,aAppend));
+  aIdent:=CreateIdentifier(aKey,amNone);
+  Result.AddKey(aIdent);
+  aIdent:=CreateIdentifier(aValue,amNone);
+  Result.AddChild(aIdent);
+end;
+
+function TBaseCSSTreeTest.CreateBinaryOperation(aOp : TCSSBinaryOperation; const aLeft, aRight: string;
+  aAppend: TAppendMode): TCSSBinaryElement;
+
+var
+  aIdent : TCSSIdentifierElement;
+
+begin
+  Result:=TCSSBinaryElement(CreateElement(TCSSBinaryElement,aAppend));
+  Result.Operation:=aOp;
+  aIdent:=CreateIdentifier(aLeft,amNone);
+  Result.Left:=aIdent;
+  aIdent:=CreateIdentifier(aRight,amNone);
+  Result.Right:=aIdent;
+end;
+
+function TBaseCSSTreeTest.CreateUnaryOperation(aOp: TCSSUnaryOperation;
+  const aRight: string; aAppend: TAppendMode): TCSSUnaryElement;
+
+var
+  aIdent : TCSSIdentifierElement;
+
+begin
+  Result:=TCSSUnaryElement(CreateElement(TCSSUnaryElement,aAppend));
+  Result.Operation:=aOp;
+  aIdent:=CreateIdentifier(aRight,amNone);
+  Result.Right:=aIdent;
+end;
+
+initialization
+  RegisterTests([TCSSTreeTypeTest,TCSSTreeAsStringTest,TCSSTreeVisitorTest]);
+end.
+

+ 29 - 0
packages/fcl-css/tests/testcss.lpr

@@ -0,0 +1,29 @@
+program testcss;
+
+{$mode objfpc}{$H+}
+
+uses
+  Classes, consoletestrunner, tccssScanner,
+tccssparser, tccsstree;
+
+type
+
+  { TMyTestRunner }
+
+  TMyTestRunner = class(TTestRunner)
+  protected
+  // override the protected methods of TTestRunner to customize its behavior
+  end;
+
+var
+  Application: TMyTestRunner;
+
+begin
+  DefaultFormat:=fPlain;
+  DefaultRunAllTests:=True;
+  Application := TMyTestRunner.Create(nil);
+  Application.Initialize;
+  Application.Title := 'CSS tests runner';
+  Application.Run;
+  Application.Free;
+end.