cScm Configuration Daemon

cScm – is a tool to convert SCM configuration files into binary format and store its in shared memory for reading by cSvn-ui and cGit-ui CGI scripts

2 Commits   0 Branches   1 Tag
author: kx <kx@radix.pro> 2023-03-24 02:53:04 +0300 committer: kx <kx@radix.pro> 2023-03-24 02:53:04 +0300 commit: 12c7b1c5658602269da2f5b75835ec0f5fab8890 parent: 4e72ffe940d9aff7c019d37a6459e765902c1fae
Commit Summary:
Version 0.1.4
Diffstat:
47 files changed, 3857 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..b345e85
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,35 @@
+
+autom4te.cache/
+
+Makefile
+Makefile.in
+config.h
+config.h.in
+config.log
+config.status
+compile
+config.guess
+config.sub
+configure
+install-sh
+missing
+stamp-h1
+aclocal.m4
+depcomp
+ylwrap
+
+cscmd/.deps/
+cscmd/Makefile
+cscmd/Makefile.in
+cscmd/cscmd.8
+cscmd/parse.c
+cscmd/parse.h
+cscmd/parse.output
+cscmd/csvn
+cscmd/cgit
+cscmd/rc.csvnd
+cscmd/rc.cgitd
+cscmd/README.csvn
+cscmd/README.cgit
+
+*~
diff --git a/.svnignore b/.svnignore
new file mode 100644
index 0000000..b345e85
--- /dev/null
+++ b/.svnignore
@@ -0,0 +1,35 @@
+
+autom4te.cache/
+
+Makefile
+Makefile.in
+config.h
+config.h.in
+config.log
+config.status
+compile
+config.guess
+config.sub
+configure
+install-sh
+missing
+stamp-h1
+aclocal.m4
+depcomp
+ylwrap
+
+cscmd/.deps/
+cscmd/Makefile
+cscmd/Makefile.in
+cscmd/cscmd.8
+cscmd/parse.c
+cscmd/parse.h
+cscmd/parse.output
+cscmd/csvn
+cscmd/cgit
+cscmd/rc.csvnd
+cscmd/rc.cgitd
+cscmd/README.csvn
+cscmd/README.cgit
+
+*~
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 0000000..cfab06b
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,17 @@
+
+ACLOCAL_AMFLAGS = -I m4
+
+#
+# In build order:
+# ==============
+#
+SUBDIRS = cscmd
+
+EXTRA_DIST = \
+             doc LICENSE README.md acsite.m4 auto-clean bootstrap \
+             cscmd/parse.y cscmd/README.in cscmd/rc.cscmd.in cscmd/logrotate.in \
+             .svnignore .gitignore
+
+nobase_include_HEADERS = cscm/bcf.h
+
+noinst_HEADERS = defs.h
diff --git a/README b/README
new file mode 100644
index 0000000..fa88f1c
--- /dev/null
+++ b/README
@@ -0,0 +1,2 @@
+
+see README.md instead
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..baa706f
--- /dev/null
+++ b/README.md
@@ -0,0 +1,214 @@
+
+# cScm Configuration Daemon
+
+**cScm** Configuration Daemon &#8211; is a tool to convert SCM configuration files into binary format and store its in shared memory
+for reading by **cSvn-ui** and **cGit-ui** CGI scripts.
+
+
+## Reqired packages
+
+**cScm** daemon depends on [libpcre2-32](https://www.pcre.org/) library. ArchLinux distribution has the **pcre2** binary package.
+On RPM based systems you can find somethink like **pcre2-devel** RPM.
+
+
+## Installation
+
+The **cScm** package provides a regular Linux daemon with control scripts.
+
+
+### Download Sources
+
+To obtain sources we have to checkout its from SVN repository:
+
+```Bash
+svn checkout svn://radix.pro/cscm/tags/cscm-0.1.4 cscm-0.1.4
+```
+and run the bootstrap script:
+
+```Bash
+cd csvn-0.1.4
+./bootstrap
+```
+Also **cScm** source packages are available for download on the
+[Radix.pro FTP-server](https://ftp.radix.pro/pub/cscm/).
+
+
+#### Bootstrap Script
+
+The *bootstrap* script especialy created for *Autotools* install automation. To install
+*Autotools* into sourse directory on build machine (i.e. when **build** == **host**) the *bootstrap*
+script can be run without arguments.
+
+```Bash
+./bootstrap
+```
+
+I this case *Autotools* will be installed from current root file system.
+
+For the cross environment the `--target-dest-dir` option allows to install some stuff from
+development root file system:
+
+```Bash
+TARGET_DEST_DIR=/home/developer/prog/trunk-672/dist/.s9xx-glibc/enybox-x2 \
+       ./bootstrap --target-dest-dir=${TARGET_DEST_DIR}
+```
+
+For example, in this case the *aclocal.m4* script will be collected from the
+`${TARGET_DEST_DIR}/usr/share/aclocal` directory.
+
+
+### Configuring Sources
+
+```Bash
+./configure --prefix=/usr \
+            --sysconfdir=/etc \
+            --with-controldir=/etc/rc.d \
+            --with-logrotatedir=/etc/logrotate.d \
+            --with-homepath=/var/lib \
+            --with-logdir=/var/log \
+            --with-piddir=/var/run
+```
+
+
+#### Install on the Build Machine
+
+```Bash
+make
+make install
+```
+
+
+#### Cross Compilation Example
+
+```Bash
+#!/bin/sh
+
+TARGET_DEST_DIR=/home/developer/prog/trunk-672/dist/.s9xx-glibc/enybox-x2
+TOOLCHAIN_PATH=/opt/toolchains/aarch64-S9XX-linux-glibc/1.1.4/bin
+TARGET=aarch64-s9xx-linux-gnu
+
+./bootstrap --target-dest-dir=${TARGET_DEST_DIR}
+
+PKG_CONFIG=/usr/bin/pkg-config \
+PKG_CONFIG_PATH=${TARGET_DEST_DIR}/usr/lib${LIBDIRSUFFIX}/pkgconfig:${TARGET_DEST_DIR}/usr/share/pkgconfig \
+PKG_CONFIG_LIBDIR=${TARGET_DEST_DIR}/usr/lib${LIBDIRSUFFIX}/pkgconfig:${TARGET_DEST_DIR}/usr/share/pkgconfig \
+STRIP="${TOOLCHAIN_PATH}/${TARGET}-strip" \
+CC="${TOOLCHAIN_PATH}/${TARGET}-gcc --sysroot=${TARGET_DEST_DIR}" \
+./configure --prefix=/usr
+  --build=x86_64-pc-linux-gnu \
+  --host=${TARGET} \
+  --sysconfdir=/etc \
+  --with-controldir=/etc/rc.d \
+  --with-logrotatedir=/etc/logrotate.d \
+  --with-homepath=/var/lib \
+  --with-logdir=/var/log \
+  --with-piddir=/var/run
+
+make
+make install DESTDIR=${TARGET_DEST_DIR}
+```
+
+Also we can make use of additional variables such as `CFLAGS`, `LDFLAGS`:
+
+```Bash
+LDFLAGS="-L${TARGET_DEST_DIR}/lib -L${TARGET_DEST_DIR}/usr/lib"
+TARGET_INCPATH="-L${TARGET_DEST_DIR}/usr/include"
+CFLAGS="${TARGET_INCPATH}"
+CPPFLAGS="${TARGET_INCPATH}"
+```
+
+### Post Install
+
+To run **cScm** daemon for some SCM engine we have to make control scripts executable:
+
+```Bash
+chmod a+x /etc/rc.d/rc.csvnd
+chmod a+x /etc/rc.d/rc.cgitd
+```
+
+If you want to run the **cscmd** daemons on boot time then on systems with BSD-like initialization such as **Slackware** we have
+to add following lines to the */etc/rc.d/rc.M* and */etc/rc.d/rc.6* scripts correspondengly:
+
+**/etc/rc.d/rc.M:**
+
+```Bash
+# Start cSvn SCM daemon:
+if [ -x /etc/rc.d/rc.csvnd ]; then
+  /etc/rc.d/rc.csvnd start
+fi
+
+# Start cGit SCM daemon:
+if [ -x /etc/rc.d/rc.cgitd ]; then
+  /etc/rc.d/rc.cgitd start
+fi
+```
+
+**/etc/rc.d/rc.6:**
+
+```Bash
+# Stop cSvn SCM daemon:
+if [ -x /etc/rc.d/rc.csvnd ]; then
+  /etc/rc.d/rc.csvnd stop
+fi
+
+# Stop cGit SCM daemon:
+if [ -x /etc/rc.d/rc.cgitd ]; then
+  /etc/rc.d/rc.cgitd stop
+fi
+```
+
+For systems which uses systemd initialization you have to setup your own systemd unit like following:
+
+**/etc/systemd/system/csvnd.service:**
+
+```ini
+[Unit]
+Description=The cSvn daemon
+After=network.target
+
+[Service]
+PIDFile=/var/run/csvnd.pid
+ExecStart=/usr/sbin/cscmd --daemonize --inotify --scm=svn --pid=/var/run/csvnd.pid --log=/var/log/csvnd.log --config=/etc/csvn-ui.rc
+ExecReload=/bin/kill -s HUP $MAINPID
+ExecStop=/bin/kill -s TERM $MAINPID
+
+[Install]
+WantedBy=multi-user.target
+```
+
+**/etc/systemd/system/cgitd.service:**
+
+```ini
+[Unit]
+Description=The cGit daemon
+After=network.target
+
+[Service]
+PIDFile=/var/run/cgitd.pid
+ExecStart=/usr/sbin/cscmd --daemonize --inotify --scm=git --pid=/var/run/cgitd.pid --log=/var/log/cgitd.log --config=/etc/cgit-ui.rc
+ExecReload=/bin/kill -s HUP $MAINPID
+ExecStop=/bin/kill -s TERM $MAINPID
+
+[Install]
+WantedBy=multi-user.target
+```
+
+
+## Binary Packages
+
+The instructions to build binary packages for popular Linux distributions are present in the
+[**Documentation**](https://csvn.radix.pro/cscm/trunk/doc/) directory where you can find **RPM** **spec** file and also
+**ArchLinux** Package Build script.
+
+
+## See Also
+
+> [**cscmd(8)**](https://csvn.radix.pro/cscm/trunk/doc/cscmd.8.md),&nbsp;
+> [**csvn-ui.rc(5)**](https://csvn.radix.pro/csvn-ui/trunk/doc/csvn-ui.rc.5.md),&nbsp;
+> [**cgit-ui.rc(5)**](https://csvn.radix.pro/cgit-ui/trunk/doc/cgit-ui.rc.5.md).
+
+
+## Copyright and License
+
+&#169; Andrey V. Kosteltsev, 2019 &#8211; 2022.<br/>
+Code and documentation released under [the **Radix.pro** License](https://csvn.radix.pro/cscm/trunk/LICENSE).
diff --git a/acsite.m4 b/acsite.m4
new file mode 100644
index 0000000..b8faf58
--- /dev/null
+++ b/acsite.m4
@@ -0,0 +1,55 @@
+
+dnl ============================================================
+dnl Display Configuration Headers
+dnl 
+dnl    configure.in:
+dnl       AC_MSG_CFG_PART(<text>)
+dnl ============================================================
+
+define(AC_MSG_CFG_PART,[dnl
+  AC_MSG_RESULT()
+  AC_MSG_RESULT(${TB}$1:${TN})
+])dnl
+
+AC_DEFUN(AC_CSCM_HEADLINE,[dnl
+  # Use a `Quadrigaph'. @<:@ gives you [ and @:>@ gives you ] :
+  TB=`echo -n -e '\033@<:@1m'`
+  TN=`echo -n -e '\033@<:@0m'`
+  echo ""
+  echo "Configuring ${TB}$1${TN} ($2), Version ${TB}${PACKAGE_VERSION}${TN}"
+  echo "$3"
+])dnl
+
+
+dnl ============================================================
+dnl  Test for build_host `ln -s' .
+dnl  ============================
+dnl
+dnl Usage:
+dnl -----
+dnl    AC_PATH_PROG_LN_S
+dnl    AC_SUBST(LN)
+dnl    AC_SUBST(LN_S)
+dnl
+dnl ============================================================
+AC_DEFUN(AC_PATH_PROG_LN_S,
+[AC_PATH_PROG(LN, ln, no, /usr/local/bin:/usr/bin:/bin:$PATH)
+AC_MSG_CHECKING(whether ln -s works on build host)
+AC_CACHE_VAL(ac_cv_path_prog_LN_S,
+[rm -f conftestdata
+if $LN -s X conftestdata 2>/dev/null
+then
+  rm -f conftestdata
+  ac_cv_path_prog_LN_S="$LN -s"
+else
+  ac_cv_path_prog_LN_S="$LN"
+fi])dnl
+LN_S="$ac_cv_path_prog_LN_S"
+if test "$ac_cv_path_prog_LN_S" = "$LN -s"; then
+  AC_MSG_RESULT(yes)
+else
+  AC_MSG_RESULT(no)
+fi
+AC_SUBST(LN)dnl
+AC_SUBST(LN_S)dnl
+])
diff --git a/auto-clean b/auto-clean
new file mode 100755
index 0000000..b100d64
--- /dev/null
+++ b/auto-clean
@@ -0,0 +1,40 @@
+#!/bin/bash
+
+CWD=`pwd`
+
+program=`basename $0`
+
+usage() {
+  cat << EOF
+
+Usage: $program [options]
+
+Options:
+  -h,--help                  Display this message.
+
+EOF
+}
+
+if [ -f "${CWD}/Makefile" ] ; then
+  make distclean
+fi
+
+svnignore='.svnignore'
+
+while read ln; do
+  line=`echo "${ln}" | sed 's,^[ \t],,' | sed 's,[ \t]$,,'`
+  if [ "x$line" != "x" -a "${line:0:1}" != "#" ] ; then
+    if `echo "${line}" | grep -q '\*~$'` ; then
+      find "`dirname "${line}"`" -type f -iname '*~' -print0 | while IFS= read -r -d '' file ; do
+        rm -f "$file"
+      done
+    elif `echo "${line}" | grep -q '\*'` ; then
+      find "`dirname "${line}"`" -type f -iname "`basename "${line}"`" -print0 | while IFS= read -r -d '' file ; do
+        rm -f "$file"
+      done
+    else
+      if [ -d "${line}" ] ; then rm -rf "${line}" ; fi
+      if [ -f "${line}" ] ; then rm  -f "${line}" ; fi
+    fi
+  fi
+done < ${CWD}/${svnignore}
diff --git a/bootstrap b/bootstrap
new file mode 100755
index 0000000..f2592c6
--- /dev/null
+++ b/bootstrap
@@ -0,0 +1,99 @@
+#!/bin/sh
+
+CWD=`pwd`
+
+program=`basename $0`
+
+usage() {
+  cat << EOF
+
+Usage: $program [options]
+
+Options:
+  -h,--help                  Display this message.
+  -d,--target-dest-dir=DIR   The target ROOTFS directory
+                             [default: DIR=/].
+
+EOF
+}
+
+TARGET_DEST_DIR=/
+ACDIR=usr/share/aclocal
+INCDIR=usr/include
+SYSTEM_ACDIR=
+SYSTEM_INCDIR=
+
+while [ 0 ] ; do
+  if [ "$1" = "-h" -o "$1" = "--help" ] ; then
+    usage
+    exit 0
+  elif [ "$1" = "-d" -o "$1" = "--target-dest-dir" ] ; then
+    if [ "$2" = "" ] ; then
+      echo -e "\n${program}: ERROR: --target-dest-dir is not specified.\n"
+      usage
+      exit 1
+    fi
+    TARGET_DEST_DIR="$2"
+    shift 2
+  elif [[ $1 == --target-dest-dir=* ]] ; then
+    TARGET_DEST_DIR="`echo $1 | cut -f2 -d'='`"
+    shift 1
+  else
+    if [ "$1" != "" ] ; then
+      echo -e "\n${program}: ERROR: Unknown argument: $1.\n"
+      usage
+      exit 1
+    fi
+    break
+  fi
+done
+
+if [ ! -d "${TARGET_DEST_DIR}" ] ; then
+  echo -e "\n${program}: ERROR: --target-dest-dir is not a directory.\n"
+  usage
+  exit 1
+fi
+
+#
+# Absolute path:
+#
+if [ "${TARGET_DEST_DIR:0:1}" != "/" ] ; then
+  TARGET_DEST_DIR=${CWD}/${TARGET_DEST_DIR}
+fi
+
+#
+# Remove last '/' char:
+#
+if [ "${TARGET_DEST_DIR: -1}" = "/" ] ; then
+  len=${#TARGET_DEST_DIR}
+  let "len = len - 1"
+  tmp="${TARGET_DEST_DIR:0:$len}"
+  TARGET_DEST_DIR=${tmp}
+fi
+
+SYSTEM_ACDIR="${TARGET_DEST_DIR}/${ACDIR}"
+SYSTEM_INCDIR="${TARGET_DEST_DIR}/${INCDIR}"
+
+
+aclocal --install -I m4 --force --system-acdir=${SYSTEM_ACDIR}
+autoheader --include=${SYSTEM_INCDIR}
+automake --foreign --add-missing --copy --force-missing
+autoconf --force
+
+#
+# Add 'cCgit Features' header to split help output:
+#
+cat >cscm-features <<_CGIT
+_ACEOF
+
+  cat <<\\_ACEOF
+
+cScm Features:
+_CGIT
+
+sed -i '/^[ ]*\-\-with\-controldir=DIR/ {
+r cscm-features
+N
+}' configure
+
+rm -f cscm-features
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 0000000..cc7eaba
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,262 @@
+
+dnl ============================================================
+dnl  Process this file with autoconf to produce
+dnl  a configure script.
+dnl ============================================================
+
+AC_PREREQ(2.71)dnl        dnl Minimum Autoconf version required.
+
+
+AC_INIT([cscm], [0.1.4],
+  [support@radix.pro], [cscm], [https://radix.pro])
+
+dnl ============================================================
+dnl m4's diversions:
+dnl ---------------
+dnl
+dnl see: /use/share/autoconf/autoconf/general.m4
+dnl ============================================================
+m4_divert_push([M4SH-INIT])
+CSVN_NAME=svn
+CSVN_CONFIG_FILE=csvn-ui.rc
+CSVN_PROGRAM=csvn
+CSVN_PROGRAM_NAME=cSvn
+CGIT_NAME=git
+CGIT_CONFIG_FILE=cgit-ui.rc
+CGIT_PROGRAM=cgit
+CGIT_PROGRAM_NAME=cGit
+PACKAGE_DAEMON=cscmd
+PACKAGE_LICENSE=Radix-1.0
+CSCM_CONTROL_DIR=/etc/rc.d
+CSCM_LOGROTATE_DIR=/etc/logrotate.d
+CSCM_HOME_PATH=/var/lib
+CSCM_LOG_DIR=/var/log
+CSCM_PID_DIR=/var/run
+m4_divert_pop([M4SH-INIT])
+
+AC_CSCM_HEADLINE([cscm], [cScm], [Copyright (c) 2019-2022 Andrey V.Kosteltsev])
+
+
+dnl ============================================================
+dnl ============================================================
+dnl ============================================================
+dnl ============================================================
+dnl ============================================================
+dnl $$                                                        $$
+dnl $$ PART: Init Automake environment                        $$
+dnl $$                                                        $$
+dnl ============================================================
+dnl ============================================================
+dnl ============================================================
+dnl ============================================================
+dnl ============================================================
+AC_MSG_CFG_PART(Init Automake environment)
+
+AC_CANONICAL_TARGET
+
+AM_INIT_AUTOMAKE([subdir-objects foreign no-dist-gzip dist-xz])
+
+AC_CONFIG_HEADERS([config.h])
+
+AC_PREFIX_DEFAULT(/usr/local)
+
+
+dnl ============================================================
+dnl ============================================================
+dnl ============================================================
+dnl ============================================================
+dnl ============================================================
+dnl $$                                                        $$
+dnl $$ PART: Test for Build Tools                             $$
+dnl $$                                                        $$
+dnl ============================================================
+dnl ============================================================
+dnl ============================================================
+dnl ============================================================
+dnl ============================================================
+AC_MSG_CFG_PART(Test for build tools)
+AC_CHECK_TOOL([GCC], [gcc], [:])
+AC_PATH_PROG([BISON], [bison], [no], [/usr/local/bin:/usr/bin:/bin:$PATH])
+test "$BISON" = "no" && AC_MSG_ERROR([Unable to find required program 'bison'])
+
+
+AC_MSG_CFG_PART(Test for Header files)
+dnl
+dnl  Check for system header files.
+dnl  =============================
+dnl  /* GetText частично проверил. Но мы делаем для себя. */
+AC_CHECK_HEADERS( locale.h )
+AC_CHECK_HEADERS( sys/types.h sys/stat.h stdlib.h stddef.h )
+AC_CHECK_HEADERS( memory.h string.h strings.h )
+AC_CHECK_HEADERS( inntypes.h stdint.h unistd.h )
+AC_CHECK_HEADERS( fcntl.h errno.h )
+AC_CHECK_HEADERS( sys/file.h )
+AC_CHECK_HEADERS( sys/uio.h )
+AC_CHECK_HEADERS( stdarg.h )
+AC_CHECK_HEADERS( sys/time.h )
+AC_CHECK_HEADERS( limits.h )
+AC_CHECK_HEADERS( endian.h )
+AC_CHECK_HEADERS( pcre2.h )
+AC_CHECK_HEADERS( wchar.h )
+AC_CHECK_HEADERS( wctype.h )
+
+dnl ============================================================
+dnl ============================================================
+dnl ============================================================
+dnl ============================================================
+dnl ============================================================
+dnl $$                                                        $$
+dnl $$ PART: Test for Libraries                               $$
+dnl $$                                                        $$
+dnl ============================================================
+dnl ============================================================
+dnl ============================================================
+dnl ============================================================
+dnl ============================================================
+AC_MSG_CFG_PART(Test for libraries)
+AC_CHECK_LIB([rt],[aio_suspend],[],[AC_MSG_ERROR([Unable to find required librt])])
+AC_CHECK_LIB([pcre2-32],[pcre2_compile_32],[],[AC_MSG_ERROR([Unable to find required libpcre2-32])])
+AC_CHECK_LIB([m],[round],[],[AC_MSG_ERROR([Unable to find required libm])])
+
+
+dnl ============================================================
+dnl ============================================================
+dnl ============================================================
+dnl ============================================================
+dnl ============================================================
+dnl $$                                                        $$
+dnl $$ PART: cScm Features                                    $$
+dnl $$                                                        $$
+dnl ============================================================
+dnl ============================================================
+dnl ============================================================
+dnl ============================================================
+dnl ============================================================
+AC_ARG_WITH([controldir],
+  [AS_HELP_STRING([--with-controldir=DIR], [cScm Daemon CONTROL directory @<:@default=/etc/rc.d@:>@],[26],[84])],
+  [controldir=$withval],
+  [controldir="/etc/rc.d"])
+AC_SUBST([controldir], [$controldir])
+AC_SUBST([CSCM_CONTROL_DIR], [$controldir])
+AC_DEFINE_UNQUOTED([CSCM_CONTROL_DIR], ["$CSCM_CONTROL_DIR"], [Define the cScm Daemon CONTROL directory])
+
+AC_ARG_WITH([logrotatedir],
+  [AS_HELP_STRING([--with-logrotatedir=DIR], [cScm Daemon LOGROTATE directory @<:@default=/etc/logrotate.d@:>@],[26],[87])],
+  [logrotatedir=$withval],
+  [logrotatedir="/etc/logrotate.d"])
+AC_SUBST([logrotatedir], [$logrotatedir])
+AC_SUBST([CSCM_LOGROTATE_DIR], [$logrotatedir])
+AC_DEFINE_UNQUOTED([CSCM_LOGROTATE_DIR], ["$CSCM_LOGROTATE_DIR"], [Define the cScm Daemon LOGROTATE directory])
+
+AC_ARG_WITH([homepath],
+  [AS_HELP_STRING([--with-homepath=DIR], [cSvn Daemon path to HOME directories @<:@default=/var/lib@:>@],[26],[84])],
+  [homepath=$withval],
+  [homepath="/var/lib"])
+AC_SUBST([homepath], [$homepath])
+AC_SUBST([CSCM_HOME_PATH], [$homepath])
+AC_DEFINE_UNQUOTED([CSCM_HOME_PATH], ["$CSCM_HOME_PATH"], [Define the cScm Daemon path to HOME directories])
+AC_SUBST([csvndhomedir], [$homepath/$CSVN_PROGRAM])
+AC_SUBST([CSVN_HOME_DIR], [$csvndhomedir])
+AC_DEFINE_UNQUOTED([CSVN_HOME_DIR], ["$CSVN_HOME_DIR"], [Define the cSvn Daemon HOME directory])
+AC_SUBST([cgitdhomedir], [$homepath/$CGIT_PROGRAM])
+AC_SUBST([CGIT_HOME_DIR], [$cgitdhomedir])
+AC_DEFINE_UNQUOTED([CGIT_HOME_DIR], ["$CGIT_HOME_DIR"], [Define the cGit Daemon HOME directory])
+
+AC_ARG_WITH([logdir],
+  [AS_HELP_STRING([--with-logdir=DIR], [cSvn Daemon LOG directory @<:@default=/var/log@:>@],[26],[84])],
+  [logdir=$withval],
+  [logdir="/var/log"])
+AC_SUBST([logdir], [$logdir])
+AC_SUBST([CSCM_LOG_DIR], [$logdir])
+AC_DEFINE_UNQUOTED([CSCM_LOG_DIR], ["$CSCM_LOG_DIR"], [Define the cScm Daemon LOG directory])
+
+AC_ARG_WITH([piddir],
+  [AS_HELP_STRING([--with-piddir=DIR], [cScm Daemon PID directory @<:@default=/var/run@:>@],[26],[84])],
+  [piddir=$withval],
+  [piddir="/var/run"])
+AC_SUBST([piddir], [$piddir])
+AC_SUBST([CSCM_PID_DIR], [$piddir])
+AC_DEFINE_UNQUOTED([CSCM_PID_DIR], ["$CSCM_PID_DIR"], [Define the cScm Daemon PID directory])
+
+AC_SUBST([CSVN_NAME], [$CSVN_NAME])
+AC_SUBST([CSVN_CONFIG], [$sysconfdir/$CSVN_CONFIG_FILE])
+AC_DEFINE_UNQUOTED([CSVN_CONFIG], ["$CSVN_CONFIG"], [Define the cSvn Default Configuration File])
+AC_SUBST([CSVN_PROGRAM], [$CSVN_PROGRAM])
+AC_DEFINE_UNQUOTED([CSVN_PROGRAM], ["$CSVN_PROGRAM"], [Define the cSvn Default Program Name])
+AC_SUBST([CSVN_PROGRAM_NAME], [$CSVN_PROGRAM_NAME])
+AC_SUBST([CGIT_NAME], [$CGIT_NAME])
+AC_SUBST([CGIT_CONFIG], [$sysconfdir/$CGIT_CONFIG_FILE])
+AC_DEFINE_UNQUOTED([CGIT_CONFIG], ["$CGIT_CONFIG"], [Define the cGit Default Configuration File])
+AC_SUBST([CGIT_PROGRAM], [$CGIT_PROGRAM])
+AC_DEFINE_UNQUOTED([CGIT_PROGRAM], ["$CGIT_PROGRAM"], [Define the cGit Default Program Name])
+AC_SUBST([CGIT_PROGRAM_NAME], [$CGIT_PROGRAM_NAME])
+
+
+AC_SUBST(PROGRAM_NAME,[${PACKAGE_NAME}])
+AC_SUBST(PROGRAM_DAEMON,[${PACKAGE_DAEMON}])
+AC_SUBST(PROGRAM_VERSION,[${PACKAGE_VERSION}])
+AC_SUBST(PROGRAM_LICENSE,[${PACKAGE_LICENSE}])
+
+AC_DEFINE_UNQUOTED([PROGRAM_NAME], ["$PROGRAM_NAME"], [Define the program name])
+AC_DEFINE_UNQUOTED([PROGRAM_DAEMON], ["$PROGRAM_DAEMON"], [Define the daemon name])
+AC_DEFINE_UNQUOTED([PROGRAM_VERSION], ["$PROGRAM_VERSION"], [Define the version of all programs in this package])
+AC_DEFINE_UNQUOTED([PROGRAM_LICENSE], ["$PROGRAM_LICENSE"], [Define the License of all programs in this package])
+
+
+dnl ============================================================
+dnl Environment Variables:
+dnl ---------------------
+dnl  For 'Some influential environment variables:' help section
+dnl ============================================================
+AC_ARG_VAR([STRIP], [strip command])
+
+
+dnl ============================================================
+dnl ============================================================
+dnl ============================================================
+dnl ============================================================
+dnl ============================================================
+dnl $$                                                        $$
+dnl $$ PART: Test for Auxiliary (my be version sensitive)     $$
+dnl $$       programs                                         $$
+dnl $$                                                        $$
+dnl ============================================================
+dnl ============================================================
+dnl ============================================================
+dnl ============================================================
+dnl ============================================================
+AC_MSG_CFG_PART(Test for aux programs)
+AC_PATH_PROG_LN_S
+AC_PATH_PROG([CAT],       [cat],       [no], [/usr/local/bin:/usr/bin:/bin:$PATH])
+AC_PATH_PROG([CHMOD],     [chmod],     [no], [/usr/local/bin:/usr/bin:/bin:$PATH])
+AC_PATH_PROG([CP],        [cp],        [no], [/usr/local/bin:/usr/bin:/bin:$PATH])
+AC_PATH_PROG([FIND],      [find],      [no], [/usr/local/bin:/usr/bin:/bin:$PATH])
+AC_PATH_PROG([GZIP],      [gzip],      [no], [/usr/local/bin:/usr/bin:/bin:$PATH])
+AC_PATH_PROG([MV],        [mv],        [no], [/usr/local/bin:/usr/bin:/bin:$PATH])
+AC_PATH_PROG([RM],        [rm],        [no], [/usr/local/bin:/usr/bin:/bin:$PATH])
+AC_PATH_PROG([SED],       [sed],       [no], [/usr/local/bin:/usr/bin:/bin:$PATH])
+AC_PATH_PROG([TAR],       [tar],       [no], [/usr/local/bin:/usr/bin:/bin:$PATH])
+AC_PATH_PROG([XZ],        [xz],        [no], [/usr/local/bin:/usr/bin:/bin:$PATH])
+
+
+dnl ============================================================
+dnl ============================================================
+dnl ============================================================
+dnl ============================================================
+dnl ============================================================
+dnl $$                                                        $$
+dnl $$ PART: OUTPUT Substitution                              $$
+dnl $$                                                        $$
+dnl ============================================================
+dnl ============================================================
+dnl ============================================================
+dnl ============================================================
+dnl ============================================================
+AC_MSG_CFG_PART(OUTPUT)
+
+AC_CONFIG_FILES([
+cscmd/Makefile
+cscmd/cscmd.8
+Makefile
+])
+AC_OUTPUT
diff --git a/cscm/bcf.h b/cscm/bcf.h
new file mode 100644
index 0000000..2ff52a9
--- /dev/null
+++ b/cscm/bcf.h
@@ -0,0 +1,123 @@
+
+#ifndef    __BCF_H
+#define    __BCF_H
+
+/******************************************
+  Binary config file format (32-bit only):
+ */
+
+#define BCF_MAG0     0x3f  /* b_ident[BI_MAG0] */
+#define BCF_MAG1      'B'  /* b_ident[BI_MAG1] */
+#define BCF_MAG2      'C'  /* b_ident[BI_MAG2] */
+#define BCF_MAG3      'F'  /* b_ident[BI_MAG3] */
+
+#define BCFMAG         "\77BCF"
+#define SZBCFMAG       4
+
+/* b_ident[BI_CLASS]: */
+#define BCF_CLASS_NONE 0  /* invalid class  */
+#define BCF_CLASS_32   1  /* 32-bit objects */
+#define BCF_CLASS_64   2  /* 64-bit objects (reserved) */
+
+/* b_ident[BI_DATA]: */
+#define BCF_DATA_NONE  0  /* invalid data encoding  */
+#define BCF_DATA_LSB   1  /* Least Significant Byte */
+#define BCF_DATA_MSB   2  /* Most Significant Byte  */
+
+#define BV_NONE        0  /* invalid version  */
+#define BV_CURRENT     1  /* current file version b_ident[BI_VERSION] */
+
+#define BCF_PAD        8  /* start of padding bytes in b_ident b_ident[BI_PAD] */
+
+#define BI_MAG0        0  /* file identification */
+#define BI_MAG1        1  /* file identification */
+#define BI_MAG2        2  /* file identification */
+#define BI_MAG3        3  /* file identification */
+#define BI_CLASS       4  /* file class */
+#define BI_DATA        5  /* data encoding */
+#define BI_VERSION     6  /* file version */
+#define BI_PAD         7  /* start of padding bytes */
+
+#define BI_NIDENT    (16)  /* size of b_ident[] */
+
+#define SI_NIDENT     (8)  /* size of b_ident[] */
+
+/* Type for a 16-bit quantity: */
+typedef uint16_t Bcf32_Half;
+
+/* Type for a 32-bit quantity: */
+typedef uint32_t Bcf32_Word;
+typedef int32_t  Bcf32_Sword;
+
+/* Type for a 64-bit quantity: */
+typedef uint64_t Bcf32_Xword;
+typedef int64_t  Bcf32_Sxword;
+
+/* Types of address: */
+typedef uint32_t Bcf32_Addr;
+
+/* Types of file offsets: */
+typedef uint32_t Bcf32_Off;
+
+typedef struct {
+  unsigned char b_ident[BI_NIDENT];
+  Bcf32_Half    b_hsize;     /* BCF header's size in bytes */
+  Bcf32_Word    b_fsize;     /* Whole BCF file size in bytes */
+
+  Bcf32_Off     b_shoff;     /* section header table’s file offset in bytes */
+  Bcf32_Half    b_shentsize; /* section header's size in bytes */
+  Bcf32_Half    b_shnum;     /* number of entries in section headers table */
+
+  Bcf32_Off     b_rhoff;     /* repository header table’s file offset in bytes */
+  Bcf32_Half    b_rhentsize; /* repository header's size in bytes */
+  Bcf32_Half    b_rhnum;     /* number of entries in repository headers table */
+
+  Bcf32_Off     b_dtoff;     /* data entries table’s file offset in bytes */
+  Bcf32_Half    b_dtentsize; /* data entry's size in bytes */
+  Bcf32_Half    b_dtnum;     /* number of entries in data entries table */
+
+  Bcf32_Off     b_stoff;     /* string table’s file offset in bytes */
+} __attribute__ ((packed)) Bcf32_fhdr;
+
+/* Type of setcions: */
+#define ST_GLOBAL 1  /* this section contains variable entries only */
+#define ST_REPOS  2  /* this section contains repository entries only */
+
+#define SMAG_GLOBAL ".global"
+#define SMAG_REPOS  ".repos"
+
+typedef struct {
+  unsigned char s_name[SI_NIDENT];
+  Bcf32_Half    s_type;  /* .global or .repos */
+  Bcf32_Off     s_shdr;  /* section header's offset in the string table */
+  Bcf32_Off     s_sdata; /* section data/repos's file offset */
+  Bcf32_Half    s_dnum;  /* number of data/repos entries in section */
+} __attribute__ ((packed)) Bcf32_shdr;
+
+typedef struct {
+  Bcf32_Off     r_rhdr;  /* repo header's offset in the string table */
+  Bcf32_Off     r_rdata; /* repo data's file offset */
+  Bcf32_Half    r_dnum;  /* number of data entries in repo */
+} __attribute__ ((packed)) Bcf32_rhdr;
+
+#define DT_NUMERICAL 0x01
+#define DT_PATH      0x02
+#define DT_STRING    0x04
+
+typedef struct {
+  Bcf32_Off     d_name;  /* variable name's offset in the string table */
+  union
+  {
+    Bcf32_Word  d_value;  /* numerical variable's value */
+    Bcf32_Off   d_valptr; /* variable value's offset in string table */
+  } _v;
+  Bcf32_Half    d_type;   /* 0x01 - numerical, 0x02 - path, 0x04 - string */
+} __attribute__ ((packed)) Bcf32_dntr;
+
+/************************
+  Shared memory (POSIX API):
+ */
+#define CSVN_SHM_BCF  "/csvn.bcf"
+#define CGIT_SHM_BCF  "/cgit.bcf"
+
+#endif  /* __BCF_H */
diff --git a/cscmd/Makefile.am b/cscmd/Makefile.am
new file mode 100644
index 0000000..6fdd496
--- /dev/null
+++ b/cscmd/Makefile.am
@@ -0,0 +1,63 @@
+
+AM_CPPFLAGS    = -I@top_srcdir@ -DYYERROR_VERBOSE=1
+
+sbin_PROGRAMS  = cscmd
+
+cscmd_SOURCES  = bconf.c daemon.c error.c lex.c main.c msglog.c symtab.c utf8ing.c xalloc.c
+
+noinst_HEADERS = bconf.h daemon.h error.h lex.h main.h msglog.h symtab.h utf8ing.h xalloc.h
+
+control_DATA   = rc.cgitd rc.csvnd
+logrotate_DATA = cgit csvn
+
+csvndhome_DATA = README.csvn
+cgitdhome_DATA = README.cgit
+
+man8_MANS = cscmd.8
+notrans_nodist_man8_MANS = cscmd.8
+
+nodist_cscmd_SOURCES = parse.c parse.h rc.csvnd csvn rc.cgitd cgit
+BUILT_SOURCES = parse.c parse.h rc.csvnd csvn rc.cgitd cgit
+
+parse.c: parse.y
+	@BISON@ -lvy --defines=parse.h -o $@ $^
+
+README.csvn: README.in
+	cat $^ | sed "s,\@CSCM_PROGRAM\@,${CSVN_PROGRAM},g" | \
+	  sed "s,\@CSCM_PROGRAM_NAME\@,${CSVN_PROGRAM_NAME},g" > $@
+
+README.cgit: README.in
+	cat $^ | sed "s,\@CSCM_PROGRAM\@,${CGIT_PROGRAM},g" | \
+	  sed "s,\@CSCM_PROGRAM_NAME\@,${CGIT_PROGRAM_NAME},g" > $@
+
+rc.csvnd: rc.cscmd.in
+	cat $^ | sed "s,\@sbindir\@,${sbindir},g" | \
+	  sed "s,\@CSCM_NAME\@,${CSVN_NAME},g" | \
+	  sed "s,\@CSCM_CONFIG\@,${CSVN_CONFIG},g" | \
+	  sed "s,\@CSCM_HOME_PATH\@,${CSCM_HOME_PATH},g" | \
+	  sed "s,\@CSCM_PID_DIR\@,${CSCM_PID_DIR},g" | \
+	  sed "s,\@CSCM_LOG_DIR\@,${CSCM_LOG_DIR},g" | \
+	  sed "s,\@CSCM_PROGRAM\@,${CSVN_PROGRAM},g" | \
+	  sed "s,\@CSCM_PROGRAM_NAME\@,${CSVN_PROGRAM_NAME},g" | \
+	  sed "s,\@PROGRAM_DAEMON\@,${PROGRAM_DAEMON},g" > $@
+
+rc.cgitd: rc.cscmd.in
+	cat $^ | sed "s,\@sbindir\@,${sbindir},g" | \
+	  sed "s,\@CSCM_NAME\@,${CGIT_NAME},g" | \
+	  sed "s,\@CSCM_CONFIG\@,${CGIT_CONFIG},g" | \
+	  sed "s,\@CSCM_HOME_PATH\@,${CSCM_HOME_PATH},g" | \
+	  sed "s,\@CSCM_PID_DIR\@,${CSCM_PID_DIR},g" | \
+	  sed "s,\@CSCM_LOG_DIR\@,${CSCM_LOG_DIR},g" | \
+	  sed "s,\@CSCM_PROGRAM\@,${CGIT_PROGRAM},g" | \
+	  sed "s,\@CSCM_PROGRAM_NAME\@,${CGIT_PROGRAM_NAME},g" | \
+	  sed "s,\@PROGRAM_DAEMON\@,${PROGRAM_DAEMON},g" > $@
+
+csvn: logrotate.in
+	cat $^ | sed "s,\@CSCM_LOG_DIR\@,${CSCM_LOG_DIR},g" | \
+	  sed "s,\@CSCM_PROGRAM\@,${CSVN_PROGRAM},g" > $@
+
+cgit: logrotate.in
+	cat $^ | sed "s,\@CSCM_LOG_DIR\@,${CSCM_LOG_DIR},g" | \
+	  sed "s,\@CSCM_PROGRAM\@,${CGIT_PROGRAM},g" > $@
+
+CLEANFILES = parse.c parse.h parse.output README.csvn rc.csvnd csvn README.cgit rc.cgitd cgit cscmd.8
diff --git a/cscmd/README.in b/cscmd/README.in
new file mode 100644
index 0000000..185a415
--- /dev/null
+++ b/cscmd/README.in
@@ -0,0 +1,5 @@
+
+@CSCM_PROGRAM_NAME@ Daemon HOME directory:
+==========================
+
+@CSCM_PROGRAM@.bcf - is a binary config file created by @CSCM_PROGRAM_NAME@ Daemon
diff --git a/cscmd/bconf.c b/cscmd/bconf.c
new file mode 100644
index 0000000..8576805
--- /dev/null
+++ b/cscmd/bconf.c
@@ -0,0 +1,477 @@
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/sysinfo.h>
+#include <sys/types.h>
+#include <stdint.h>
+#include <dirent.h>
+#include <sys/stat.h> /* chmod(2)    */
+#include <sys/file.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <string.h>   /* strdup(3)   */
+#include <libgen.h>   /* basename(3) */
+#include <ctype.h>    /* tolower(3)  */
+#include <errno.h>
+#include <time.h>
+#include <sys/time.h>
+#include <pwd.h>
+#include <grp.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <endian.h>
+
+#include <error.h>
+#include <msglog.h>
+#include <xalloc.h>
+#include <symtab.h>
+#include <parse.h>
+#include <bconf.h>
+
+#include <defs.h>
+
+extern const char *SHM_BCF;
+
+FILE *bcf = NULL;
+char *bcf_fname = NULL;
+
+static void *bcf_shm_address = NULL;
+static int   bcf_shm_fd      = -1;
+
+static int  snum, rnum, dnum, global_dnum, global_rnum, indent;
+
+static unsigned char *ftab, *stab = NULL;
+static Bcf32_Off      stabsz = 0;
+
+static Bcf32_Off  shoff; /* section header table’s file offset in bytes */
+static Bcf32_Off  rhoff; /* repository header table’s file offset in bytes */
+static Bcf32_Off  dtoff; /* data entries table’s file offset in bytes */
+static Bcf32_Off  stoff; /* string table’s file offset in bytes */
+
+static Bcf32_fhdr *fhdr;
+static Bcf32_shdr *shdr;
+static Bcf32_rhdr *rhdr;
+static Bcf32_dntr *dntr;
+
+
+void bcf_shm_free( void )
+{
+  if( bcf_shm_address )
+  {
+    struct stat st;
+
+    if( !fstat( bcf_shm_fd, (struct stat *)&st ) )
+    {
+      (void)munmap( bcf_shm_address, (size_t)st.st_size );
+      bcf_shm_address = NULL;
+      bcf_shm_fd = shm_unlink( SHM_BCF );
+    }
+  }
+  bcf_shm_address = NULL;
+  bcf_shm_fd = -1;
+}
+
+/************************************************
+  Функции создания BCF файла по таблице symlist:
+ */
+
+static Bcf32_Off extend_strtab( const char *val )
+{
+  Bcf32_Off      off  = 1;
+  Bcf32_Off      len  = 0;
+  unsigned char *dest = NULL;
+
+  if( !stab )
+  {
+    /*************************************
+      The first string in strtab is equal
+      to "" for empty strings.
+     */
+    stabsz = (Bcf32_Off)(strlen( val ) + 2);
+    stab = (unsigned char *)xmalloc( (size_t)stabsz );
+    (void)strncpy( (char *)&stab[1], val, stabsz - 1 );
+    return off;
+  }
+  else
+  {
+    off     = stabsz;
+    len     = (Bcf32_Off)(strlen( val ) + 1);
+    stabsz += len;
+
+    stab = (unsigned char *)xrealloc( (void *)stab, (size_t)stabsz );
+    dest = &stab[off];
+    (void)strncpy( (char *)dest, val, len );
+    return off;
+  }
+}
+
+static void count_symbols( int *snum, int *rnum, int *dnum, int *gdts, int *grps, SYMBOL *list )
+{
+  SYMBOL *head = list;
+
+  if( !head ) return;
+
+  while( head )
+  {
+    /************************************
+      count symbols( head ):
+     */
+    switch( head->type )
+    {
+      case STRING:
+        if( indent == 0 ) *gdts += 1;
+        *dnum += 1;
+        break;
+      case PATH:
+        if( indent == 0 ) *gdts += 1;
+        *dnum += 1;
+        break;
+      case NUMERICAL:
+        if( indent == 0 ) *gdts += 1;
+        *dnum += 1;
+        break;
+
+      case SECTION:
+        *snum += 1;
+        break;
+      case REPO:
+        if( indent == 0 ) *grps += 1;
+        *rnum += 1;
+        break;
+
+      default:
+        break;
+    }
+
+    if( head->list ) { indent += 1; count_symbols( snum, rnum, dnum, gdts, grps, head->list ); }
+    /*
+      End of count symbols( head ).
+     ************************************/
+
+    head = head->next;
+  }
+}
+
+static void write_global_data( SYMBOL *list )
+{
+  SYMBOL *head = list;
+
+  if( !head ) return;
+
+  while( head )
+  {
+    /************************************
+      global symbols( head ):
+     */
+    switch( head->type )
+    {
+      case STRING:
+        dntr->d_name      = extend_strtab( (const char *)head->name );
+        dntr->d_type      = DT_STRING;
+        dntr->_v.d_valptr = extend_strtab( (const char *)head->u.string );
+        dtoff += (Bcf32_Off)sizeof( Bcf32_dntr );
+        ++dntr;
+        break;
+      case PATH:
+        dntr->d_name      = extend_strtab( (const char *)head->name );
+        dntr->d_type      = DT_PATH;
+        dntr->_v.d_valptr = extend_strtab( (const char *)head->u.path );
+        dtoff += (Bcf32_Off)sizeof( Bcf32_dntr );
+        ++dntr;
+        break;
+      case NUMERICAL:
+        dntr->d_name      = extend_strtab( (const char *)head->name );
+        dntr->d_type      = DT_NUMERICAL;
+        dntr->_v.d_value  = head->u.value;
+        dtoff += (Bcf32_Off)sizeof( Bcf32_dntr );
+        ++dntr;
+        break;
+
+      default:
+        break;
+    }
+    /*
+      End of global symbols( head ).
+     ************************************/
+
+    head = head->next;
+  }
+}
+
+static Bcf32_Half write_repo_data( SYMBOL *list )
+{
+  Bcf32_Half  cntr = 0;
+  SYMBOL     *head = list;
+
+  if( !head ) return cntr;
+
+  while( head )
+  {
+    /************************************
+      symbols( head ):
+     */
+    switch( head->type )
+    {
+      case STRING:
+        dntr->d_name      = extend_strtab( (const char *)head->name );
+        dntr->d_type      = DT_STRING;
+        dntr->_v.d_valptr = extend_strtab( (const char *)head->u.string );
+        dtoff += (Bcf32_Off)sizeof( Bcf32_dntr );
+        ++dntr;
+        ++cntr;
+        break;
+      case PATH:
+        dntr->d_name      = extend_strtab( (const char *)head->name );
+        dntr->d_type      = DT_PATH;
+        dntr->_v.d_valptr = extend_strtab( (const char *)head->u.path );
+        dtoff += (Bcf32_Off)sizeof( Bcf32_dntr );
+        ++dntr;
+        ++cntr;
+        break;
+      case NUMERICAL:
+        dntr->d_name      = extend_strtab( (const char *)head->name );
+        dntr->d_type      = DT_NUMERICAL;
+        dntr->_v.d_value  = head->u.value;
+        dtoff += (Bcf32_Off)sizeof( Bcf32_dntr );
+        ++dntr;
+        ++cntr;
+        break;
+
+      default:
+        break;
+    }
+    /*
+      End of symbols( head ).
+     ************************************/
+
+    head = head->next;
+  }
+
+  return cntr;
+}
+
+static void write_global_repos( SYMBOL *list )
+{
+  SYMBOL *head = list;
+
+  if( !head ) return;
+
+  while( head )
+  {
+    /************************************
+      global symbols( head ):
+     */
+    if( head->type == REPO )
+    {
+      rhdr->r_rhdr  = extend_strtab( (const char *)head->u.path );
+      rhdr->r_rdata = dtoff;
+      rhdr->r_dnum  = write_repo_data( head->list );
+
+      rhoff += (Bcf32_Off)sizeof( Bcf32_rhdr );
+      ++rhdr;
+    }
+    /*
+      End of global symbols( head ).
+     ************************************/
+
+    head = head->next;
+  }
+}
+
+
+static Bcf32_Half write_repos( SYMBOL *list )
+{
+  Bcf32_Half  cntr = 0;
+  SYMBOL     *head = list;
+
+  if( !head ) return cntr;
+
+  while( head )
+  {
+    /************************************
+      symbols( head ):
+     */
+    if( head->type == REPO )
+    {
+      rhdr->r_rhdr  = extend_strtab( (const char *)head->u.path );
+      rhdr->r_rdata = dtoff;
+      rhdr->r_dnum  = write_repo_data( head->list );
+
+      rhoff += (Bcf32_Off)sizeof( Bcf32_rhdr );
+      ++rhdr;
+      ++cntr;
+    }
+    /*
+      End of symbols( head ).
+     ************************************/
+
+    head = head->next;
+  }
+
+  return cntr;
+}
+
+static void write_sections( SYMBOL *list )
+{
+  SYMBOL *head = list;
+
+  if( !head ) return;
+
+  while( head )
+  {
+    /************************************
+      global symbols( head ):
+     */
+    if( head->type == SECTION )
+    {
+      (void)strncpy( (char *)shdr->s_name, SMAG_REPOS, (size_t)SI_NIDENT );
+      shdr->s_type  = ST_REPOS;
+      shdr->s_shdr  = extend_strtab( (const char *)head->u.string );
+      shdr->s_sdata = rhoff;
+      shdr->s_dnum  = write_repos( head->list );;
+
+      shoff += (Bcf32_Off)sizeof( Bcf32_shdr );
+      ++shdr;
+    }
+    /*
+      End of global symbols( head ).
+     ************************************/
+
+    head = head->next;
+  }
+}
+
+
+int write_binary_config( void )
+{
+  int ret = 0;
+
+  ftab = NULL;
+  stab = NULL;
+
+  snum = 0, rnum = 0, dnum = 0, global_dnum = 0, global_rnum = 0, indent = 0, stabsz = 0;
+  fhdr = NULL, shdr = NULL, rhdr = NULL, dntr = NULL;
+  shoff = 0, rhoff = 0, dtoff = 0, stoff = 0;
+
+  count_symbols( &snum, &rnum, &dnum, &global_dnum, &global_rnum, symlist );
+
+  if( global_dnum ) snum += 1; /* add .global section for global variables   */
+  if( global_rnum ) snum += 1; /* add noname .repos section for global repositories */
+
+  shoff = (Bcf32_Off)sizeof( Bcf32_fhdr );
+  rhoff = (Bcf32_Off)(shoff + snum * sizeof( Bcf32_shdr ));
+  dtoff = (Bcf32_Off)(rhoff + rnum * sizeof( Bcf32_rhdr ));
+  stoff = (Bcf32_Off)(dtoff + dnum * sizeof( Bcf32_dntr ));
+
+  ftab = (unsigned char *)xmalloc( (size_t)stoff );
+
+  /******************
+    Fill File Header
+   */
+  fhdr = (Bcf32_fhdr *)ftab;
+
+  (void)strncpy( (char *)fhdr->b_ident, BCFMAG, (size_t)SZBCFMAG );
+  fhdr->b_ident[BI_CLASS]   = BCF_CLASS_32;
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+  fhdr->b_ident[BI_DATA]    = BCF_DATA_LSB;
+#else
+  fhdr->b_ident[BI_DATA]    = BCF_DATA_MSB;
+#endif
+  fhdr->b_ident[BI_VERSION] = BV_CURRENT;
+  fhdr->b_ident[BI_PAD]     = BCF_PAD;
+
+  fhdr->b_hsize     = (Bcf32_Half)sizeof( Bcf32_fhdr );
+  fhdr->b_shoff     = (Bcf32_Off)shoff;
+  fhdr->b_shentsize = (Bcf32_Half)sizeof( Bcf32_shdr );
+  fhdr->b_shnum     = (Bcf32_Half)snum;
+  fhdr->b_rhoff     = (Bcf32_Off)rhoff;
+  fhdr->b_rhentsize = (Bcf32_Half)sizeof( Bcf32_rhdr );
+  fhdr->b_rhnum     = (Bcf32_Half)rnum;
+  fhdr->b_dtoff     = (Bcf32_Off)dtoff;
+  fhdr->b_dtentsize = (Bcf32_Half)sizeof( Bcf32_dntr );
+  fhdr->b_dtnum     = (Bcf32_Half)dnum;
+  fhdr->b_stoff     = (Bcf32_Off)stoff;
+
+  shdr = (Bcf32_shdr *)&ftab[shoff];
+  rhdr = (Bcf32_rhdr *)&ftab[rhoff];
+  dntr = (Bcf32_dntr *)&ftab[dtoff];
+
+  if( global_dnum )
+  {
+    (void)strncpy( (char *)shdr->s_name, SMAG_GLOBAL, (size_t)SI_NIDENT );
+    shdr->s_type  = ST_GLOBAL;
+    shdr->s_shdr  = 0; /* Global section is always a first noname .global section */
+    shdr->s_sdata = fhdr->b_dtoff;
+    shdr->s_dnum  = (Bcf32_Half)global_dnum;
+
+    write_global_data( symlist );
+
+    shoff += (Bcf32_Off)sizeof( Bcf32_shdr );
+    ++shdr;
+  }
+
+  if( global_rnum )
+  {
+    (void)strncpy( (char *)shdr->s_name, SMAG_REPOS, (size_t)SI_NIDENT );
+    shdr->s_type  = ST_REPOS;
+    shdr->s_shdr  = 0; /* Global repos plased in the second noname .repos section */
+    shdr->s_sdata = fhdr->b_rhoff;
+    shdr->s_dnum  = (Bcf32_Half)global_rnum;
+
+    write_global_repos( symlist );
+
+    shoff += (Bcf32_Off)sizeof( Bcf32_shdr );
+    ++shdr;
+  }
+
+  write_sections( symlist );
+
+  /**********************
+    Whole BCF file size:
+   */
+  fhdr->b_fsize = (Bcf32_Word)( stoff + stabsz );
+
+  bcf_shm_free();
+
+  bcf_shm_fd = shm_open( SHM_BCF, O_CREAT | O_TRUNC | O_RDWR, 0644 );
+  if( bcf_shm_fd != -1 )
+  {
+    (void)ftruncate( bcf_shm_fd, (size_t)fhdr->b_fsize );
+    bcf_shm_address = mmap( NULL, (size_t)fhdr->b_fsize, PROT_WRITE, MAP_SHARED, bcf_shm_fd, 0 );
+    if( bcf_shm_address != MAP_FAILED )
+    {
+      memcpy( bcf_shm_address, (const void *)ftab, (size_t)stoff );
+      memcpy( bcf_shm_address + (size_t)stoff, (const void *)stab, (size_t)stabsz );
+    }
+  }
+
+  if( bcf_fname )
+  {
+    bcf = fopen( (const char *)bcf_fname, "w" );
+    if( !bcf ) { FATAL_ERROR( "Cannot open BCF file: %s", bcf_fname ); }
+  }
+
+  (void)fwrite( (void *)ftab, (size_t)stoff,  1, bcf );
+  (void)fwrite( (void *)stab, (size_t)stabsz, 1, bcf );
+
+  if( bcf_fname )
+  {
+    if( bcf ) { fclose( bcf ); bcf = NULL; } /* Do not free bcf_fname[] */
+  }
+
+  if( ftab ) { free( ftab ); ftab = NULL; }
+  if( stab ) { free( stab ); stab = NULL; }
+
+  shoff = 0, rhoff = 0, dtoff = 0, stoff = 0;
+  fhdr = NULL, shdr = NULL, rhdr = NULL, dntr = NULL;
+  snum = 0, rnum = 0, dnum = 0, global_dnum = 0, global_rnum = 0, indent = 0, stabsz = 0;
+
+  ret = 1; /* success */
+
+  return ret;
+}
diff --git a/cscmd/bconf.h b/cscmd/bconf.h
new file mode 100644
index 0000000..cd8b49d
--- /dev/null
+++ b/cscmd/bconf.h
@@ -0,0 +1,25 @@
+
+#ifndef    __BCONF_H
+#define    __BCONF_H
+
+#include <cscm/bcf.h>
+
+
+extern FILE *bcf;
+extern char *bcf_fname;
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+extern int   write_binary_config( void );
+extern void  bcf_shm_free( void );
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  /* __BCONF_H */
diff --git a/cscmd/cscmd.8.in b/cscmd/cscmd.8.in
new file mode 100644
index 0000000..f9fb862
--- /dev/null
+++ b/cscmd/cscmd.8.in
@@ -0,0 +1,66 @@
+.\"
+.TH "CSCMD" 8 "2022-02-19" "cScm Configuration Daemon" "cscmd"
+
+.SH "NAME"
+\fBcscmd\fR \- cScm Configurations Daemon
+
+.SH "SYNOPSIS"
+.PP
+\fB\fBcscmd\fR [\fBOPTIONS\fR]\fR \fB\-\-scm\fR=[\fIsvn\fR|\fIgit\fR]
+
+.SH "SUMMARY"
+\fBcscmd\fR should be run at boot time by \fI@sysconfdir@/rc.d/rc.csvnd\fR or \fI@sysconfdir@/rc.d/rc.cgitd\fR.
+This daemon read the config file \fI@sysconfdir@/csvn-ui.rc\fR or \fI@sysconfdir@/cgit-ui.rc\fR (depends on \fB\-\-scm\fR option)
+and convert it to binary (see \fI@includedir@/cscm/bcf.h\fR) form for cSvn or cGit CGI scripts.
+
+
+.SH "OPTIONS"
+
+.TP
+\fB-h\fR,\fB--help\fR
+Display help information.
+
+.TP
+\fB-v\fR,\fB--version\fR
+Display the version of \fBcscm\fR daemon.
+
+.TP
+\fB-d\fR,\fB--daemonize\fR
+Run in background as a daemon.
+
+.TP
+\fB-i\fR,\fB--inotify\fR
+Notify about configuration changes. If this option is set then \fBcscmd\fR daemon selects changes made
+in \fI@sysconfdir@/csvn-ui.rc\fR or \fI@sysconfdir@/cgit-ui.rc\fR config file and reread configuration when changes is done.
+Without this option rereading configuration file can be done by sending \fB-HUP\fR to the \fBcscmd\fR daemon.
+
+.TP
+\fB-b\fR,\fB--bcf\fR=\fB<BCF_FILE>\fR
+Binary config file (depends on \fB\-\-scm\fR option). Default: \fI@CSCM_HOME_PATH@/csvn/csvn.bcf\fR.
+
+.TP
+\fB-c\fR,\fB--config\fR=\fB<CONFIG_FILE>\fR
+Config file (depends on \fB\-\-scm\fR option). Default: \fI@sysconfdir@/csvn-ui.rc\fR.
+
+.TP
+\fB-l\fR,\fB--log\fR=\fB<LOG_FILE>\fR
+Log file (depends on \fB\-\-scm\fR option). Default: \fI@CSCM_LOG_DIR@/csvnd.log\fR.
+
+.TP
+\fB-p\fR,\fB--pid\fR=\fB<PID_FILE>\fR
+Log file (depends on \fB\-\-scm\fR option). Default: \fI@CSCM_PID_DIR@/csvnd.pid\fR.
+
+.TP
+\fB-s\fR,\fB--scm\fR=\fB[svn|git]\fR
+SCM engine name: \fIsvn\fR or \fIgit\fR. Default: \fIsvn\fR.
+
+.TP
+\fB-t\fR,\fB--test\fR
+Test the config file and exit.
+
+
+.SH "SEE ALSO"
+.BR csvn-ui.rc(5),
+.BR cgit-ui.rc(5)
+
+
diff --git a/cscmd/daemon.c b/cscmd/daemon.c
new file mode 100644
index 0000000..80ad4ed
--- /dev/null
+++ b/cscmd/daemon.c
@@ -0,0 +1,39 @@
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <fcntl.h>
+#include <paths.h>
+#include <unistd.h>
+
+#include <daemon.h>
+
+int daemon( int nochdir, int noclose )
+{
+  int fd;
+
+  switch( fork() )
+  {
+    case -1:
+      return( -1 );
+    case 0:
+      break;
+    default:
+      _exit( 0 ); /* direct use kernel exit */
+  }
+
+  if( setsid() == -1 ) return( -1 );
+  if( !nochdir ) chdir( "/" );
+  if( noclose ) return( 0 );
+
+  fd = open( _PATH_DEVNULL, O_RDWR, 0 );
+  if( fd != -1 )
+  {
+    dup2( fd, STDIN_FILENO );
+    dup2( fd, STDOUT_FILENO );
+    dup2( fd, STDERR_FILENO );
+    if( fd > 2 ) close( fd );
+  }
+  return( 0 );
+}
diff --git a/cscmd/daemon.h b/cscmd/daemon.h
new file mode 100644
index 0000000..83dd044
--- /dev/null
+++ b/cscmd/daemon.h
@@ -0,0 +1,7 @@
+
+#ifndef   __DAEMON_H__
+#define   __DAEMON_H__
+
+extern int daemon( int, int );
+
+#endif /* __DAEMON_H__ */
diff --git a/cscmd/error.c b/cscmd/error.c
new file mode 100644
index 0000000..ec469c9
--- /dev/null
+++ b/cscmd/error.c
@@ -0,0 +1,92 @@
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdarg.h>
+#include <limits.h>
+#include <locale.h>
+#include <wchar.h>
+#include <wctype.h>
+
+#include <defs.h>
+
+#include <error.h>
+#include <msglog.h>
+#include <utf8ing.h>
+#include <lex.h>
+
+
+extern char *config_fname;
+
+int errors   = 0;
+int warnings = 0;
+
+
+void error( char *fmt, ... )
+{
+  va_list arg_ptr;
+  char  buf[MAX_ERROR_MSG_SIZE];
+  char  msg[MAX_ERROR_MSG_SIZE];
+  char *format = "%s:%d:%d: %s";
+
+  va_start( arg_ptr, fmt );
+
+  vsnprintf( msg, MAX_ERROR_MSG_SIZE, (const void *)fmt, arg_ptr );
+
+  va_end( arg_ptr ); /* Reset variable arguments. */
+
+  snprintf( buf, MAX_ERROR_MSG_SIZE, format, config_fname, lineno, colno, msg );
+
+  ERROR( "%s", buf );
+
+  ++errors;
+}
+
+void warning( char *fmt, ... )
+{
+  va_list arg_ptr;
+  char  buf[MAX_ERROR_MSG_SIZE];
+  char  msg[MAX_ERROR_MSG_SIZE];
+  char *format = "%s:%d:%d: %s";
+
+  va_start( arg_ptr, fmt );
+
+  vsnprintf( msg, MAX_ERROR_MSG_SIZE, (const void *)fmt, arg_ptr );
+
+  va_end( arg_ptr ); /* Reset variable arguments. */
+
+  snprintf( buf, MAX_ERROR_MSG_SIZE, format, config_fname, lineno, colno, msg );
+
+  WARNING( "%s", buf );
+
+  ++warnings;
+}
+
+void no_space( void )
+{
+  char  buf[MAX_ERROR_MSG_SIZE];
+  char *format = "%s: Cannot allocate memory";
+
+  snprintf( buf, MAX_ERROR_MSG_SIZE, format, config_fname );
+
+  FATAL_ERROR( "%s", buf );
+
+  ++errors;
+}
+
+void unterminated_comment( void )
+{
+  char  buf[MAX_ERROR_MSG_SIZE];
+  char *format = "%s:%d:%d: Unterminated comment";
+
+  snprintf( buf, MAX_ERROR_MSG_SIZE, format, config_fname, lineno, colno );
+
+  ERROR( "%s", buf );
+
+  ++errors;
+}
diff --git a/cscmd/error.h b/cscmd/error.h
new file mode 100644
index 0000000..866ec2a
--- /dev/null
+++ b/cscmd/error.h
@@ -0,0 +1,25 @@
+
+#ifndef    __ERROR_H
+#define    __ERROR_H
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define MAX_ERROR_MSG_SIZE  PATH_MAX
+
+extern int errors;
+extern int warnings;
+
+extern void error( char *fmt, ... );
+extern void warning( char *fmt, ... );
+extern void no_space( void );
+extern void unterminated_comment( void );
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  /* __ERROR_H */
diff --git a/cscmd/lex.c b/cscmd/lex.c
new file mode 100644
index 0000000..318e074
--- /dev/null
+++ b/cscmd/lex.c
@@ -0,0 +1,815 @@
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdarg.h>
+#include <limits.h>
+#include <locale.h>
+#include <wchar.h>
+#include <wctype.h>
+
+#define PCRE2_CODE_UNIT_WIDTH 32
+#include <pcre2.h>
+
+#include <defs.h>
+
+#include <main.h>
+#include <error.h>
+#include <msglog.h>
+#include <xalloc.h>
+#include <utf8ing.h>
+#include <symtab.h>
+#include <parse.h>
+
+#include <lex.h>
+
+
+
+int lineno = 0;
+int colno  = 0;
+
+static int       maxtoken;
+static wchar_t  *token_buffer;
+
+static int       max8token;
+static utf8_t   *token_utf8_buffer;
+
+int       indent_level = 0; /* Number of '{' minus number of '}'. */
+
+static int       end_of_file = 0;
+static int       nextchar = -1;
+
+static char     *locale;
+
+#define GETC(c)    ({ wint_t ret; ++colno; ret = fgetwc( config ); ret; })
+#define UNGETC(c)  ({ wint_t ret; --colno; ret = ungetwc( c, config ); ret; })
+
+
+static wchar_t *extend_token_buffer( wchar_t *p )
+{
+   int offset = p - token_buffer;
+   maxtoken = maxtoken * 2 + 10;
+   token_buffer = (wchar_t *)xrealloc( token_buffer, (maxtoken + 2)*sizeof(wchar_t) );
+
+   return( token_buffer + offset );
+}
+
+static utf8_t *extend_token_utf8_buffer( utf8_t *p )
+{
+   int offset = p - token_utf8_buffer;
+   max8token = max8token * 2 + 10;
+   token_utf8_buffer = (utf8_t *)xrealloc( token_utf8_buffer, (max8token + 2)*6 );
+
+   return( token_utf8_buffer + offset );
+}
+
+
+void yyerror( char const *s )
+{
+  error( "%s", s );
+}
+
+
+void init_lex( void )
+{
+  locale = setlocale( LC_ALL, "en_US.utf8" );
+
+  lineno = 0;
+  colno  = 0;
+
+  nextchar  = -1;
+  maxtoken  = 40;
+  max8token = 40;
+
+  indent_level = 0;
+  end_of_file  = 0;
+
+  token_buffer = (wchar_t *)xmalloc( maxtoken * sizeof(wchar_t) + 2 );
+  token_utf8_buffer = (utf8_t *)xmalloc( max8token * 6 + 2 );
+}
+
+void fini_lex( void )
+{
+  locale = setlocale( LC_ALL, locale );
+
+  if( token_buffer ) { free( token_buffer ); token_buffer = NULL; }
+  if( token_utf8_buffer ) { free( token_utf8_buffer ); token_utf8_buffer = NULL; }
+
+  indent_level = 0;
+  end_of_file  = 0;
+
+  max8token =  0;
+  maxtoken  =  0;
+  nextchar  = -1;
+
+  lineno = 0;
+  colno  = 0;
+}
+
+static wint_t check_newline( void )
+{
+  wint_t  c;
+
+  ++lineno;
+  colno  = 0; /* считает GETC()/UNGETC(); здесь надо только обнулить */
+
+  /*****************************************
+    Read first nonwhite char on the line.
+   *****************************************/
+  c = GETC();
+  while( c == ' ' || c == '\t' ) c = GETC();
+
+  if( c == '#' ) goto skipline;
+  else           return( c );
+
+  /* skip the rest of this line */
+skipline:
+
+  while( c != '\n' && c != WEOF )
+    c = GETC();
+
+  return( c );
+}
+
+static wint_t skip_comment( int c )
+{
+  if( c == '*' )
+  {
+do1:
+    do
+    {
+       c = GETC();
+       if( c == '\n' ) { ++lineno; colno = 0; }
+
+    } while( c != '*' && c != WEOF );
+
+    if( c == WEOF )
+    {
+       unterminated_comment();
+       return( WEOF );
+    }
+
+    c = GETC();
+
+    if( c == '/' )
+    {
+       c = GETC();
+       if( c == '\n' ) c = check_newline();
+       return( c );
+    }
+    else
+    {
+       UNGETC( c );
+       goto do1;
+    }
+  }
+  else if( c == '/' || c == '#' )
+  {
+    do
+    {
+       c = GETC();
+
+    } while( c != '\n' && c != WEOF );
+
+    if( c == WEOF )
+    {
+       unterminated_comment();
+       return( WEOF );
+    }
+    else c = check_newline();
+
+    return( c );
+  }
+
+  return( c );
+
+} /* End skip_commemnt() */
+
+static wint_t skip_white_space( wint_t c )
+{
+  for( ;; )
+  {
+    switch( c )
+    {
+      case '\n':
+        c = check_newline();
+        break;
+
+      case '#':
+        c = skip_comment( c );
+        return( skip_white_space( c ) );
+        break;
+
+      case '/':
+        c = GETC();
+        if( c == '/' || c == '*' )
+        {
+          c = skip_comment( c );
+          return( skip_white_space( c ) );
+        }
+        else
+        {
+          UNGETC( c );
+          return( '/' );
+        }
+        break;
+
+      case ' ':
+      case '\t':
+      case '\f':
+      case '\v':
+      case '\b':
+      case '\r':
+        c = GETC();
+        break;
+      case '\\':
+        c = GETC();
+        if( c == '\n' ) { ++lineno; colno = 0; }
+        else
+        {
+          warning( "%s", "Stray '\\' in program" );
+        }
+        c = GETC();
+        break;
+      default:
+        return( c );
+
+    } /* End switch( c ) */
+
+  } /* End for( ;; ) */
+
+} /* End skip_white_space() */
+
+static wint_t readescape( int *ignore_ptr )
+/*
+   read escape sequence, returning a char, or store 1 in *ignore_ptr
+   if it is backslash-newline
+ */
+{
+  wint_t    c = GETC();
+  wint_t    code;
+  unsigned  count;
+  unsigned  firstdig = 0;
+  int       nonull;
+
+  switch( c )
+  {
+     case 'x':
+        code   = 0;
+        count  = 0;
+        nonull = 0;
+        while( 1 )
+        {
+           c = GETC();
+           if( !(c >= 'a' && c <= 'f') &&
+               !(c >= 'A' && c <= 'F') &&
+               !(c >= '0' && c <= '9')   )
+           {
+              UNGETC( c );
+              break;
+           }
+           code *= 16;
+           if( c >= 'a' && c <= 'f' ) code += c - 'a' + 10;
+           if( c >= 'A' && c <= 'F' ) code += c - 'A' + 10;
+           if( c >= '0' && c <= '9' ) code += c - '0';
+           if( code != 0 || count != 0 )
+           {
+              if( count == 0 ) firstdig = code;
+              count++;
+           }
+           nonull = 1;
+
+        } /* End while( 1 ) */
+
+        if( !nonull )
+        {
+           error( "%s", "\\x used with no following hex digits" );
+        }
+        else if( count == 0 )
+           /* Digits are all 0's. Ok. */
+           ;
+        else if( (count - 1) * 4 >= 32 || /* 32 == bits per INT */
+                 (count > 1 && ((1 << (32 - (count-1) * 4)) <= firstdig )))
+        {
+           warning( "%s", "Hex escape out of range" );
+        }
+        return( code );
+
+     case '0': case '1': case '2': case '3': case '4':
+     case '5': case '6': case '7':
+        code  = 0;
+        count = 0;
+        while( (c <= '7') && (c >= '0') && (count++ < 6) )
+        {
+           code = (code * 8) + (c - '0');
+           c = GETC();
+        }
+        UNGETC( c );
+        return( code );
+
+     case '\\': case '\'': case '"':
+        return( c );
+
+     case '\n':
+        lineno++; colno = 0;
+        *ignore_ptr = 1;
+        return( 0 );
+
+     case 'n':
+        return( '\n' );
+
+     case 't':
+        return( '\t' );
+
+     case 'r':
+        return( '\r' );
+
+     case 'f':
+        return( '\f' );
+
+     case 'b':
+        return( '\b' );
+
+     case 'a':
+        return( '\a' );
+
+     case 'v':
+        return( '\v' );
+  }
+
+  return( c );
+
+} /* End of readescape() */
+
+
+int html_symbol_name( wchar_t *str )
+{
+  int         rc = 0, error = 0;
+  PCRE2_SIZE  offset = 0;
+  wchar_t     pattern[] = L"^(&[#A-Za-z0-9]*;)";
+
+  pcre2_match_data *match;
+
+  pcre2_code *regexp = pcre2_compile( (PCRE2_SPTR)pattern, PCRE2_ZERO_TERMINATED, 0, &error, &offset, NULL );
+  if( regexp == NULL )
+  {
+    return 0; /* PCRE compilation failed */
+  }
+
+  match = pcre2_match_data_create_from_pattern( regexp, NULL );
+
+  rc = pcre2_match( regexp, (PCRE2_SPTR)str, (int)wcslen(str), 0, 0, match, NULL );
+  if( rc < 0 )
+  {
+    /* not match */
+    pcre2_match_data_free( match );
+    pcre2_code_free( regexp );
+    return 0;
+  }
+  else
+  {
+    /* match */
+    pcre2_match_data_free( match );
+    pcre2_code_free( regexp );
+    return 1;
+  }
+}
+
+
+int yylex( void )
+{
+  wint_t   c;
+  wchar_t *p;
+  int      value;
+
+  if( nextchar >= 0 )
+    c = nextchar, nextchar = -1;
+  else
+    c = GETC();
+
+  while( 1 )
+  {
+    switch( c )
+    {
+      case ' ':
+      case '\t':
+      case '\f':
+      case '\v':
+      case '\b':
+        c = skip_white_space( c );
+        break;
+
+      case '\r':
+      case '\n':
+      case '/':
+  case '#':
+      case '\\':
+        c = skip_white_space( c );
+
+      default:
+        goto found_nonwhite;
+
+    } /* End switch( c ) */
+found_nonwhite:
+
+    token_buffer[0] = c;
+    token_buffer[1] = 0;
+
+    switch( c )
+    {
+      case WEOF:
+        end_of_file = 1;
+        token_buffer[0] = 0;
+        value = 0;
+        goto done;
+        break;
+
+      case '$': /* dollar in identifier */
+        if( 1 ) goto letter;
+        return '$';
+
+      case 'A': case 'B': case 'C': case 'D': case 'E':
+      case 'F': case 'G': case 'H': case 'I': case 'J':
+      case 'K': case 'L': case 'M': case 'N': case 'O':
+      case 'P': case 'Q': case 'R': case 'S': case 'T':
+      case 'U': case 'V': case 'W': case 'X': case 'Y':
+      case 'Z':
+      case 'a': case 'b': case 'c': case 'd': case 'e':
+      case 'f': case 'g': case 'h': case 'i': case 'j':
+      case 'k': case 'l': case 'm': case 'n': case 'o':
+      case 'p': case 'q': case 'r': case 's': case 't':
+      case 'u': case 'v': case 'w': case 'x': case 'y':
+      case 'z':
+      case '_':
+
+      /* RUSSIAN */
+      case L'А': case L'Б': case L'В': case L'Г': case L'Д':
+      case L'Е': case L'Ё': case L'Ж': case L'З': case L'И':
+      case L'Й': case L'К': case L'Л': case L'М': case L'Н':
+      case L'О': case L'П': case L'Р': case L'С': case L'Т':
+      case L'У': case L'Ф': case L'Х': case L'Ц': case L'Ч':
+      case L'Ш': case L'Щ': case L'Ъ': case L'Ы': case L'Ь':
+      case L'Э': case L'Ю': case L'Я':
+
+      case L'а': case L'б': case L'в': case L'г': case L'д':
+      case L'е': case L'ё': case L'ж': case L'з': case L'и':
+      case L'й': case L'к': case L'л': case L'м': case L'н':
+      case L'о': case L'п': case L'р': case L'с': case L'т':
+      case L'у': case L'ф': case L'х': case L'ц': case L'ч':
+      case L'ш': case L'щ': case L'ъ': case L'ы': case L'ь':
+      case L'э': case L'ю': case L'я':
+
+letter:
+        p = token_buffer;
+        while( iswalnum( c ) || c == '_' || c == '$' || c == '@' || c == '-' || c == '.' || c == ':' )
+        {
+          if( p >= token_buffer + maxtoken )
+          {
+            p = extend_token_buffer( p );
+            extend_token_utf8_buffer( token_utf8_buffer );
+          }
+
+          *p++ = c;
+          c = GETC();
+        }
+        *p = 0;
+        nextchar = c;
+        value = VARIABLE;
+
+        (void)copy_ucs4_to_utf8( (utf8_t *)token_utf8_buffer, (const ucs4_t *)token_buffer );
+
+        /*********************
+          install into symtab
+         *********************/
+        {
+          if( !strcmp( "section", (const char *)token_utf8_buffer ) )
+          {
+            value = SECTION;
+            yylval.sym = install( NULL, SECTION, NULL );
+          }
+          else if( !strcmp( "repo", (const char *)token_utf8_buffer ) )
+          {
+            value = REPO;
+            yylval.sym = install( NULL, REPO, NULL );
+          }
+          else
+          {
+            SYMBOL *sp = NULL;
+
+            if( (sp = lookup( (const char *)token_utf8_buffer )) == (SYMBOL *)0 )
+              sp = install( (const char *)token_utf8_buffer, VARIABLE, 0 );
+
+            /******************************************************************
+              Если переменная уже в таблице, то мы предполагаем, что она имеет
+              тип равный одному из допустимых: NUMERICAL, STRING, или PATH.
+             ******************************************************************/
+            if( sp->type != VARIABLE )
+            {
+              switch( sp->type )
+              {
+                case NUMERICAL:
+                case STRING:
+                case PATH:
+                  value = sp->type;
+                  break;
+                default:
+                  /* error */
+                  break;
+              }
+            }
+            yylval.sym = sp;
+          }
+        }
+
+        token_buffer[0] = 0;
+        token_utf8_buffer[0] = 0;
+        goto done;
+        break;
+
+      case '0': case '1': case '2': case '3': case '4':
+      case '5': case '6': case '7': case '8': case '9':
+        {
+          int constant = 0;
+/* integer: */
+          p = token_buffer;
+          while( iswdigit( c ) )
+          {
+            if( p >= token_buffer + maxtoken )
+            {
+              p = extend_token_buffer( p );
+              extend_token_utf8_buffer( token_utf8_buffer );
+            }
+
+            *p++ = c;
+            c = GETC();
+          }
+          *p = 0;
+          nextchar = c;
+          value = NUMERICAL;
+
+          (void)copy_ucs4_to_utf8( (utf8_t *)token_utf8_buffer, (const ucs4_t *)token_buffer );
+
+          /*********************
+            install into symtab
+           *********************/
+          {
+            (void)swscanf( (const wchar_t *)token_buffer, L"%d", &constant );
+            yylval.sym = install( NULL, NUMERICAL, constant );
+          }
+
+          token_buffer[0] = 0;
+          token_utf8_buffer[0] = 0;
+          goto done;
+          break;
+        }
+
+      case '\'':
+/* path_constant: */
+        {
+          int           num_chars = 0;
+          unsigned int  width = 8; /* to allow non asscii in path set width = 16 */
+
+          while( 1 )
+          {
+tryagain:
+            c = GETC();
+
+            if( c == '\'' || c == WEOF ) break;
+            if( c == '\\' )
+            {
+              int ignore = 0;
+              c = readescape( &ignore );
+              if( ignore ) goto tryagain;
+              if( (unsigned)c >= (1 << width) )
+              {
+                warning( "%s", "Escape sequence out of range" );
+              }
+            }
+            else if( c == '\n' ) { lineno++; colno = 0; }
+
+            num_chars++;
+            if( num_chars > maxtoken - 4 )
+            {
+              extend_token_buffer( token_buffer );
+              extend_token_utf8_buffer( token_utf8_buffer );
+            }
+
+            token_buffer[num_chars] = c;
+
+          } /* End while( 1 ) */
+
+          token_buffer[num_chars + 1] = '\'';
+          token_buffer[num_chars + 2] = 0;
+
+          if( c != '\'' )
+          {
+            error( "%s", "Malformated path constant" );
+          }
+          else if( num_chars == 0 )
+          {
+            error( "%s", "Empty path constant" );
+          }
+
+          /* build path: */
+          {
+            wchar_t *s, *string = NULL;
+            wchar_t *p = &token_buffer[0];
+
+            while( *p )
+            {
+              if( *p == '\n' || *p == '\t' ) *p = ' ';
+              ++p;
+            }
+
+            string = (wchar_t *)malloc( maxtoken * 4 + 10 );
+
+            p = &token_buffer[1];
+            s = &string[0];
+
+            while( *p == ' ' ) ++p;
+
+            while( *p )
+            {
+              if( *p != ' ' )
+                *s++ = *p++;
+              else
+                ++p;
+            }
+            --s; *s = 0;
+            while( *(s-1) == ' ' ) --s;
+            *s = 0;
+
+            (void)copy_ucs4_to_utf8( (utf8_t *)token_utf8_buffer, (const ucs4_t *)string );
+
+            free( string );
+          }
+
+          /*********************
+            install into symtab
+           *********************/
+          {
+            yylval.sym = install( NULL, PATH, (char *)token_utf8_buffer );
+          }
+
+          token_buffer[0] = 0;
+          token_utf8_buffer[0] = 0;
+          value = PATH;
+          goto done;
+        }
+
+      case '"':
+/* string_constant: */
+        {
+          c = GETC();
+          p = token_buffer + 1;
+
+          while( c != '"' && c >= 0 )
+          {
+            if( c == '\\' )
+            {
+              int ignore = 0;
+              c = readescape( &ignore );
+              if( ignore ) goto skipnewline;
+            }
+            else if( c == '\n' ) lineno++;
+
+            if( p == token_buffer + maxtoken )
+            {
+              p = extend_token_buffer( p );
+              extend_token_utf8_buffer( token_utf8_buffer );
+            }
+            *p++ = c;
+
+skipnewline:
+            c = GETC();
+
+          } /* End while( " ) */
+
+          *p = 0;
+
+          if( c < 0 )
+          {
+            error( "%s", "Unterminated string constant" );
+          }
+
+
+          *p++ = '"';
+          *p = 0;
+
+          /* build string: */
+          {
+            wchar_t *s, *string = NULL;
+            wchar_t *p = &token_buffer[0];
+
+            while( *p )
+            {
+              if( *p == '\n' || *p == '\t' ) *p = ' ';
+              ++p;
+            }
+
+            string = (wchar_t *)malloc( maxtoken * 4 + 10 );
+
+            p = &token_buffer[1];
+            s = &string[0];
+
+            while( *p == ' ' ) ++p;
+
+            while( *p )
+            {
+              if( *p != ' ' )
+              {
+                switch( *p )
+                {
+                  case '&':
+                    /************************************************
+                      Skip HTML symbol names such as &nbsp,... etc.:
+                     */
+                    if( ! html_symbol_name( p ) )
+                    {
+                      *s++ = '&'; *s++ = 'a'; *s++ = 'm'; *s++ = 'p'; *s++ = ';'; ++p;
+                    }
+                    else
+                    {
+                      *s++ = *p++;
+                    }
+                    break;
+
+                  case '<':
+                    *s++ = '&'; *s++ = 'l'; *s++ = 't'; *s++ = ';'; ++p;
+                    break;
+
+                  case '>':
+                    *s++ = '&'; *s++ = 'g'; *s++ = 't'; *s++ = ';'; ++p;
+                    break;
+
+                  default:
+                    *s++ = *p++;
+                    break;
+                }
+              }
+              else
+              {
+                /* skip multiple spaces */
+                if( *(p+1) != ' ' )
+                  *s++ = *p++;
+                else
+                  ++p;
+              }
+            }
+            --s; *s = 0;
+            while( *(s-1) == ' ' ) --s;
+            *s = 0;
+
+            (void)copy_ucs4_to_utf8( (utf8_t *)token_utf8_buffer, (const ucs4_t *)string );
+
+            free( string );
+          }
+
+          /*********************
+            install into symtab
+           *********************/
+          {
+            yylval.sym = install( NULL, STRING, (char *)token_utf8_buffer );
+          }
+
+          token_buffer[0] = 0;
+          token_utf8_buffer[0] = 0;
+          value = STRING;
+          goto done;
+        }
+
+      case 0:
+        value = 1;
+        goto done;
+        break;
+
+      case '{':
+        indent_level++;
+        value = c;
+        goto done;
+        break;
+
+      case '}':
+        indent_level--;
+        value = c;
+        goto done;
+        break;
+
+      default:
+        value = c;
+        goto done;
+        break;
+
+    } /* End switch( c ) */
+
+  } /* End while( 1 ) */
+
+done:
+
+   return( value );
+}
diff --git a/cscmd/lex.h b/cscmd/lex.h
new file mode 100644
index 0000000..5c686e2
--- /dev/null
+++ b/cscmd/lex.h
@@ -0,0 +1,26 @@
+
+#ifndef    __LEX_H
+#define    __LEX_H
+
+
+extern int lineno;
+extern int colno;
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+extern void init_lex( void );
+extern void fini_lex( void );
+
+extern int yylex( void );
+extern void yyerror( char const *s );
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  /* __LEX_H */
diff --git a/cscmd/logrotate.in b/cscmd/logrotate.in
new file mode 100644
index 0000000..4d36ac5
--- /dev/null
+++ b/cscmd/logrotate.in
@@ -0,0 +1,8 @@
+
+@CSCM_LOG_DIR@/@CSCM_PROGRAM@d.log {
+  rotate 7
+  size=5M
+  compress
+  notifempty
+  missingok
+}
diff --git a/cscmd/main.c b/cscmd/main.c
new file mode 100644
index 0000000..9058046
--- /dev/null
+++ b/cscmd/main.c
@@ -0,0 +1,737 @@
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/sysinfo.h>
+#include <sys/types.h>
+#include <stdint.h>
+#include <dirent.h>
+#include <sys/stat.h> /* chmod(2)    */
+#include <fcntl.h>
+#include <limits.h>
+#include <string.h>   /* strdup(3)   */
+#include <libgen.h>   /* basename(3) */
+#include <ctype.h>    /* tolower(3)  */
+#include <errno.h>
+#include <time.h>
+#include <sys/time.h>
+#include <pwd.h>
+#include <grp.h>
+#include <stdarg.h>
+#include <unistd.h>
+
+#include <sys/inotify.h>
+
+#include <locale.h>
+#include <wchar.h>
+#include <wctype.h>
+
+#include <sys/wait.h>
+
+#include <sys/resource.h>
+
+#include <signal.h>
+#if !defined SIGCHLD && defined SIGCLD
+# define SIGCHLD SIGCLD
+#endif
+
+#define _GNU_SOURCE
+#include <getopt.h>
+
+
+#include <daemon.h>
+#include <msglog.h>
+#include <error.h>
+#include <utf8ing.h>
+#include <symtab.h>
+#include <parse.h>
+#include <lex.h>
+#include <bconf.h>
+
+#include <defs.h>
+
+
+/*********************
+  Default File Names:
+ */
+const char *CONFIG_FILE = CSVN_CONFIG;
+const char *BCF_FILE    = CSVN_HOME_DIR "/" CSVN_PROGRAM ".bcf";
+const char *LOG_FILE    = CSCM_LOG_DIR  "/" CSVN_PROGRAM "d.log";
+const char *PID_FILE    = CSCM_PID_DIR  "/" CSVN_PROGRAM "d.pid";
+const char *SHM_BCF     = CSVN_SHM_BCF;
+
+
+char *program = PROGRAM_DAEMON;
+int   exit_status = EXIT_SUCCESS; /* errors counter */
+
+FILE *config = NULL;
+char *config_fname = NULL;
+
+char *log_fname = NULL;
+char *pid_fname = NULL;
+
+static sigset_t  blockmask;
+
+static int  run_as_daemon = 0;
+static int  test_config_file = 0;
+static int  config_inotify = 0;
+
+
+static void free_resources( void );
+
+
+/***********************
+  Inotify declarations:
+ */
+#define IN_BUFFER_SIZE  (7 * (sizeof(struct inotify_event) + NAME_MAX + 1))
+
+static int inotify_fd = 0, wd = 0;
+static struct inotify_event *event = NULL;
+static char buf[IN_BUFFER_SIZE] __attribute__ ((aligned(8)));
+
+static int check_event( struct inotify_event *e )
+{
+  if( e->mask & IN_CLOSE_WRITE ) return 1;
+  return 0;
+}
+
+static void init_config_inotify( void )
+{
+  inotify_fd = inotify_init(); /* Create inotify instance */
+  if( inotify_fd == -1 )
+  {
+    ERROR( "Cannot initialize inotify for file: %s", config_fname );
+    LOG( "Stop cScm Configuration Daemon." );
+    free_resources();
+    exit( 1 );
+  }
+
+  wd = inotify_add_watch( inotify_fd, config_fname, IN_CLOSE_WRITE );
+  if( wd == -1 )
+  {
+    ERROR( "Cannot add inotify watch for file: %s", config_fname );
+    LOG( "Stop cScm Configuration Daemon." );
+    free_resources();
+    exit( 1 );
+  }
+}
+
+static void fini_config_inotify( void )
+{
+  if( wd > 0 )         { (void)inotify_rm_watch( inotify_fd, wd ); wd = 0; }
+  if( inotify_fd > 0 ) { (void)close( inotify_fd ); inotify_fd = 0;        }
+}
+
+
+static void free_resources( void )
+{
+  fini_config_inotify();
+
+  if( log_fname )
+  {
+    if( errlog ) { fclose( errlog ); errlog = NULL; }
+    free( log_fname ); log_fname = NULL;
+  }
+  if( config_fname )
+  {
+    if( config ) { fclose( config ); config = NULL; }
+    free( config_fname ); config_fname = NULL;
+  }
+  bcf_shm_free();
+  if( bcf_fname )
+  {
+    if( bcf ) { fclose( bcf ); bcf = NULL; }
+    (void)unlink( (const char *)bcf_fname );
+    free( bcf_fname ); bcf_fname = NULL;
+  }
+  if( pid_fname )
+  {
+    (void)unlink( (const char *)pid_fname );
+    free( pid_fname ); pid_fname = NULL;
+  }
+}
+
+
+void usage()
+{
+  free_resources();
+
+  fprintf( stdout, "\n" );
+  fprintf( stdout, "Usage: %s [options]\n", program );
+  fprintf( stdout, "\n" );
+  fprintf( stdout, "Start cScm Configuration Daemon to read config file.\n" );
+  fprintf( stdout, "\n" );
+  fprintf( stdout, "Options:\n" );
+  fprintf( stdout, "  -h,--help                     Display this information.\n" );
+  fprintf( stdout, "  -v,--version                  Display the version of %s utility.\n", program );
+  fprintf( stdout, "  -d,--daemonize                Run in background as a daemon.\n" );
+  fprintf( stdout, "  -i,--inotify                  Notify about configuration changes.\n" );
+  fprintf( stdout, "  -b,--bcf=<BCF_FILE>           Binary config file. Default: %s.\n", BCF_FILE );
+  fprintf( stdout, "  -c,--config=<CONFIG_FILE>     Config file. Default: %s.\n", CONFIG_FILE );
+  fprintf( stdout, "  -l,--log=<LOG_FILE>           Log file. Default: %s.\n", LOG_FILE );
+  fprintf( stdout, "  -p,--pid=<PID_FILE>           PID file. Default: %s.\n", PID_FILE );
+  fprintf( stdout, "  -s,--scm=<SCM>                SCM 'svn' or 'git'. Default: 'svn'.\n" );
+  fprintf( stdout, "  -t,--test                     Test config file and exit.\n" );
+  fprintf( stdout, "\n" );
+
+  exit( EXIT_FAILURE );
+}
+
+void to_lowercase( char *s )
+{
+  char *p = s;
+  while( p && *p ) { int c = *p; *p = tolower( c ); ++p; }
+}
+
+void to_uppercase( char *s )
+{
+  char *p = s;
+  while( p && *p ) { int c = *p; *p = toupper( c ); ++p; }
+}
+
+void version()
+{
+  char *upper = NULL;
+
+  upper = (char *)alloca( strlen( program ) + 1 );
+
+  strcpy( (char *)upper, (const char *)program );
+  to_uppercase( upper );
+
+  fprintf( stdout, "%s (%s) %s\n", program, upper, PROGRAM_VERSION );
+
+  fprintf( stdout, "Copyright (C) 2022 Andrey V.Kosteltsev.\n" );
+  fprintf( stdout, "This is free software.   There is NO warranty; not even\n" );
+  fprintf( stdout, "for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n" );
+  fprintf( stdout, "\n" );
+
+  free_resources();
+  exit( EXIT_SUCCESS );
+}
+
+void get_args( int argc, char *argv[] )
+{
+  const char* short_options = "hvdic:l:p:s:t";
+
+  const struct option long_options[] =
+  {
+    { "help",               no_argument,       NULL, 'h' },
+    { "version",            no_argument,       NULL, 'v' },
+    { "daemonize",          no_argument,       NULL, 'd' },
+    { "inotify",            no_argument,       NULL, 'i' },
+    { "bcf",                required_argument, NULL, 'b' },
+    { "config",             required_argument, NULL, 'c' },
+    { "log",                required_argument, NULL, 'l' },
+    { "pid",                required_argument, NULL, 'p' },
+    { "scm",                required_argument, NULL, 's' },
+    { "test",               no_argument,       NULL, 't' },
+    { NULL,                 0,                 NULL,  0  }
+  };
+
+  int ret;
+  int option_index = 0;
+
+  while( (ret = getopt_long( argc, argv, short_options, long_options, &option_index )) != -1 )
+  {
+    switch( ret )
+    {
+      case 'h':
+      {
+        usage();
+        break;
+      }
+      case 'v':
+      {
+        version();
+        break;
+      }
+      case 'd':
+      {
+        run_as_daemon = 1;
+        break;
+      }
+      case 'i':
+      {
+        config_inotify = 1;
+        break;
+      }
+      case 't':
+      {
+        test_config_file = 1;
+        break;
+      }
+
+      case 'b':
+      {
+        if( optarg != NULL )
+        {
+          bcf_fname = strdup( optarg );
+        }
+        else
+          /* option is present but without value */
+          usage();
+        break;
+      }
+
+      case 'c':
+      {
+        if( optarg != NULL )
+        {
+          config_fname = strdup( optarg );
+        }
+        else
+          /* option is present but without value */
+          usage();
+        break;
+      }
+
+      case 'l':
+      {
+        if( optarg != NULL )
+        {
+          log_fname = strdup( optarg );
+        }
+        else
+          /* option is present but without value */
+          usage();
+        break;
+      }
+
+      case 'p':
+      {
+        if( optarg != NULL )
+        {
+          pid_fname = strdup( optarg );
+        }
+        else
+          /* option is present but without value */
+          usage();
+        break;
+      }
+
+      case 's':
+      {
+        if( optarg != NULL )
+        {
+          if( *optarg && !strncmp( optarg, "git", 3 ) )
+          {
+            CONFIG_FILE = CGIT_CONFIG;
+               BCF_FILE = CGIT_HOME_DIR "/" CGIT_PROGRAM ".bcf";
+               LOG_FILE = CSCM_LOG_DIR  "/" CGIT_PROGRAM "d.log";
+               PID_FILE = CSCM_PID_DIR  "/" CGIT_PROGRAM "d.pid";
+                SHM_BCF = CGIT_SHM_BCF;
+          }
+        }
+        else
+          /* option is present but without value */
+          usage();
+        break;
+      }
+
+      case '?': default:
+      {
+        usage();
+        break;
+      }
+    }
+  }
+}
+
+
+static int is_directory( const char *path )
+{
+  struct stat st;
+  return ( !stat( path, &st ) && S_ISDIR(st.st_mode) );
+}
+
+static void logpid( void )
+{
+  FILE *fp;
+
+  if( !pid_fname )
+  {
+    /* allocate memory for PID file name */
+    pid_fname = (char *)malloc( strlen( PID_FILE ) + 1 );
+    if( !pid_fname ) { FATAL_ERROR( "Cannot allocate memory for PID file name: %s", PID_FILE ); }
+    (void)strcpy( pid_fname, PID_FILE );
+  }
+
+  /* Create CSCM_PID_DIR if not exists: */
+  {
+    char  *pid_dir = NULL, *dir = strdup( pid_fname );
+
+    pid_dir = dirname( dir );
+
+    if( !is_directory( (const char *)pid_dir ) )
+    {
+      /* Create if not exists */
+      if( 0 != mkdir( (const char *)pid_dir, 0755 ) )
+      {
+        FATAL_ERROR( "Cannot create directory for PID file: %s", PID_FILE );
+      }
+    }
+    free( dir );
+  }
+
+  if( (fp = fopen( pid_fname, "w" )) != NULL )
+  {
+    fprintf( fp, "%u\n", getpid() );
+    (void)fclose( fp );
+  }
+}
+
+void init_logs( void )
+{
+  /* print to stderr until the errlog file is open */
+  errlog = stderr;
+
+  if( !log_fname )
+  {
+    /* allocate memory for LOG file name */
+    log_fname = (char *)malloc( strlen( LOG_FILE ) + 1 );
+    if( !log_fname ) { FATAL_ERROR( "Cannot open log file: %s", LOG_FILE ); }
+    (void)strcpy( log_fname, LOG_FILE );
+  }
+
+  /* Create CSCM_LOG_DIR if not exists: */
+  {
+    char  *log_dir = NULL, *dir = strdup( log_fname );
+
+    log_dir = dirname( dir );
+
+    if( !is_directory( (const char *)log_dir ) )
+    {
+      /* Create if not exists */
+      if( 0 != mkdir( (const char *)log_dir, 0755 ) )
+      {
+        FATAL_ERROR( "Cannot create directory for log file: %s", LOG_FILE );
+      }
+    }
+    free( dir );
+  }
+
+  /* open LOG file */
+  errlog = fopen( (const char *)log_fname, "a" );
+  if( !errlog ) { errlog = stderr; FATAL_ERROR( "Cannot open log file: %s", log_fname ); }
+}
+
+void open_config_file( void )
+{
+  if( !config_fname )
+  {
+    /* allocate memory for CONFIG file name */
+    config_fname = (char *)malloc( strlen( CONFIG_FILE ) + 1 );
+    if( !config_fname )
+    {
+      FATAL_ERROR( "Cannot open config file: %s", CONFIG_FILE );
+      if( log_fname ) { fclose( errlog ); free( log_fname ); }
+    }
+    (void)strcpy( config_fname, CONFIG_FILE );
+  }
+
+  /* open CONFIG file */
+  config = fopen( (const char *)config_fname, "r" );
+  if( !config )
+  {
+    FATAL_ERROR( "Cannot open config file: %s", config_fname );
+    if( log_fname ) { fclose( errlog ); free( log_fname ); }
+  }
+
+  if( !bcf_fname )
+  {
+    /* allocate memory for BCF file name */
+    bcf_fname = (char *)malloc( strlen( BCF_FILE ) + 1 );
+    if( !bcf_fname )
+    {
+      FATAL_ERROR( "Cannot allocate memory gor BCF file name: %s", BCF_FILE );
+      if( log_fname ) { fclose( errlog ); free( log_fname ); }
+    }
+    (void)strcpy( bcf_fname, BCF_FILE );
+  }
+}
+
+void close_config_file( void )
+{
+  if( config ) { fclose( config ); config = NULL; }
+}
+
+
+
+void parse_config_file( void )
+{
+  int ret;
+
+  /***********************************
+    Blick signals until parser works:
+   */
+  (void)sigprocmask( SIG_BLOCK, (const sigset_t *)&blockmask, (sigset_t *)NULL );
+
+  open_config_file();
+  init_symtab();
+  init_lex();
+
+  if( !(ret = yyparse()) )
+  {
+    reverse_symlist( (SYMBOL **)&symlist );
+    remove_consts( (SYMBOL **)&symlist );
+    (void)write_binary_config();
+  }
+  else
+  {
+    bcf_shm_free();
+    if( bcf_fname )
+    {
+      if( bcf ) { fclose( bcf ); bcf = NULL; }
+      (void)unlink( (const char *)bcf_fname );
+    }
+  }
+
+  fini_lex();
+  fini_symtab();
+  close_config_file();
+
+  (void)sigprocmask( SIG_UNBLOCK, (const sigset_t *)&blockmask, (sigset_t *)NULL );
+
+  if( test_config_file )
+  {
+    if( ret )
+    {
+      fprintf( stdout, "%s: %s: Config file is not correct. See: %s\n", program, config_fname, log_fname );
+      LOG( "Stop cScm Configuration Daemon." );
+      free_resources();
+      exit( 1 );
+    }
+    else
+    {
+      fprintf( stdout, "%s: %s: Config file is correct.\n", program, config_fname );
+      LOG( "Stop cScm Configuration Daemon." );
+      free_resources();
+      exit( 0 );
+    }
+  }
+}
+
+
+
+void fatal_error_actions( void )
+{
+  free_resources();
+}
+
+void sigint( int signum )
+{
+  (void)signum;
+
+  LOG( "received SIGINT: free resources at exit" );
+  LOG( "Stop cScm Configuration Daemon." );
+
+  free_resources();
+  exit( 0 );
+}
+
+void sigusr( int signum )
+{
+  if( signum == SIGUSR1 )
+  {
+    LOG( "signal USR1 has been received" );
+  }
+  else if( signum == SIGUSR2 )
+  {
+    LOG( "signal USR2 has been received" );
+  }
+}
+
+void sighup( int signum )
+{
+  (void)signum;
+
+  LOG( "received SIGHUP: parse config file: %s", config_fname );
+
+  parse_config_file();
+}
+
+static void set_signal_handlers()
+{
+  struct sigaction  sa;
+  sigset_t          set;
+
+  memset( &sa, 0, sizeof( sa ) );
+  sa.sa_handler = sighup;          /* HUP: read config file */
+  sa.sa_flags = SA_RESTART;
+  sigemptyset( &set );
+  sigaddset( &set, SIGHUP );
+  sa.sa_mask = set;
+  sigaction( SIGHUP, &sa, NULL );
+
+  memset( &sa, 0, sizeof( sa ) );
+  sa.sa_handler = sigusr;          /* USR1, USR2 */
+  sa.sa_flags = SA_RESTART;
+  sigemptyset( &set );
+  sigaddset( &set, SIGUSR1 );
+  sigaddset( &set, SIGUSR2 );
+  sa.sa_mask = set;
+  sigaction( SIGUSR1, &sa, NULL );
+  sigaction( SIGUSR2, &sa, NULL );
+
+  memset( &sa, 0, sizeof( sa ) );
+  sa.sa_handler = sigint;          /* TERM, INT */
+  sa.sa_flags = SA_RESTART;
+  sigemptyset( &set );
+  sigaddset( &set, SIGTERM );
+  sigaddset( &set, SIGINT );
+  sa.sa_mask = set;
+  sigaction( SIGTERM, &sa, NULL );
+  sigaction( SIGINT, &sa,  NULL );
+
+  memset( &sa, 0, sizeof( sa ) );  /* ignore SIGPIPE */
+  sa.sa_handler = SIG_IGN;
+  sa.sa_flags = 0;
+  sigaction( SIGPIPE, &sa, NULL );
+
+  /* на случай блокировки сигналов с помощью sigprocmask(): */
+  sigemptyset( &blockmask );
+  sigaddset( &blockmask, SIGHUP );
+  sigaddset( &blockmask, SIGUSR1 );
+  sigaddset( &blockmask, SIGUSR2 );
+  sigaddset( &blockmask, SIGTERM );
+  sigaddset( &blockmask, SIGINT );
+
+  /* System V fork+wait does not work if SIGCHLD is ignored */
+  signal( SIGCHLD, SIG_DFL );
+}
+
+
+
+int main( int argc, char *argv[] )
+{
+  gid_t  gid;
+
+  set_signal_handlers();
+
+  gid = getgid();
+  setgroups( 1, &gid );
+
+  fatal_error_hook = fatal_error_actions;
+
+  program = basename( argv[0] );
+  get_args( argc, argv );
+
+  if( getppid() != 1 )
+  {
+    if( run_as_daemon ) daemon( 1, 0 );
+  }
+
+  init_logs();
+  logpid();
+
+  LOG( "Start cScm Configuration Daemon..." );
+
+  parse_config_file();
+
+  if( config_inotify ) init_config_inotify();
+
+  for( ;; )
+  {
+    int    max_sd;
+    struct timeval timeout;
+    fd_set listen_set;
+
+    /*************************************
+      Waiting for events from inotify_fd:
+     */
+    max_sd = inotify_fd + 1;
+    FD_ZERO( &listen_set );
+    FD_SET( inotify_fd, &listen_set );
+
+    do
+    {
+      int rc;
+
+      /*********************************************
+        Initialize the timeval struct to 5 seconds:
+       */
+      timeout.tv_sec  = 5;
+      timeout.tv_usec = 0;
+
+      rc = select( max_sd, &listen_set, NULL, NULL, &timeout );
+
+      /*****************************************
+        Check to see if the select call failed:
+       */
+      if( rc < 0 && errno != EINTR )
+      {
+        WARNING( "%s: inotify select() failed", config_fname );
+        break;
+      }
+
+      /************************************************
+        Check to see if the 5 second time out expired:
+       */
+      if( rc == 0 )
+      {
+        /* Here we can output some log info. */
+        break;
+      }
+
+      if( FD_ISSET( inotify_fd, &listen_set ) )
+      {
+        ssize_t  n;
+        char    *p;
+
+        n = read( inotify_fd, buf, IN_BUFFER_SIZE );
+        if( n == 0 )
+        {
+          ERROR( "%s: read() from inotify file descriptor returned '0'", config_fname );
+          LOG( "Stop cScm Configuration Daemon." );
+          free_resources();
+          exit( 1 );
+        }
+        if( n == -1 )
+        {
+          ERROR( "%s: read() from inotify file descriptor returned '-1'", config_fname );
+          LOG( "Stop cScm Configuration Daemon." );
+          free_resources();
+          exit( 1 );
+        }
+
+        for( p = buf; p < buf + n; )
+        {
+          event = (struct inotify_event *)p;
+          /************************************************************
+            в принципе, нам хватает одного события и, если мы получили
+            нужное событие и перечитали config file, то здесь мы можем
+            выйти из цикла чтения событий с помощью break
+           */
+          if( check_event( event ) )
+          {
+            LOG( "Config file '%s' has been changed. Read new config content...", config_fname );
+            parse_config_file();
+            break;
+          }
+          p += sizeof(struct inotify_event) + event->len;
+        }
+
+      } /* End if( FD_ISSET() ) */
+
+    } while( 1 );
+
+    /*
+      Здесь мы можем выполнить действия, которые необходимы
+      в том случае, если в течение 5-и секунд мы не получили
+      ни одного сигнала об изменении дескриптора inotify_fd.
+     */
+    {
+      sleep( 5 );
+    }
+
+  } /* End of waiting for( ;; ) */
+
+
+  LOG( "Stop cScm Configuration Daemon." );
+  free_resources();
+
+  return 0;
+}
diff --git a/cscmd/main.h b/cscmd/main.h
new file mode 100644
index 0000000..75404a4
--- /dev/null
+++ b/cscmd/main.h
@@ -0,0 +1,21 @@
+
+#ifndef    __MAIN_H
+#define    __MAIN_H
+
+
+extern FILE *config;
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  /* __MAIN_H */
diff --git a/cscmd/msglog.c b/cscmd/msglog.c
new file mode 100644
index 0000000..2c4a821
--- /dev/null
+++ b/cscmd/msglog.c
@@ -0,0 +1,70 @@
+
+/**********************************************************************
+
+  Copyright 2019 Andrey V.Kosteltsev
+
+  Licensed under the Radix.pro License, Version 1.0 (the "License");
+  you may not use this file  except  in compliance with the License.
+  You may obtain a copy of the License at
+
+     https://radix.pro/licenses/LICENSE-1.0-en_US.txt
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+  implied.
+
+ **********************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <time.h>
+#include <sys/time.h>
+
+#include <msglog.h>
+
+FILE *errlog;
+
+void (*fatal_error_hook)( void );
+
+void logmsg( FILE *logfile, enum _msg_type type, char *format, ... )
+{
+  va_list argp;
+
+  if( ! format ) return;
+
+  {
+    time_t     t = time( NULL );
+    struct tm tm = *localtime(&t);
+
+    fprintf( logfile, "[%04d-%02d-%02d %02d:%02d:%02d]: ",
+                        tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
+                                       tm.tm_hour, tm.tm_min, tm.tm_sec );
+  }
+
+  switch( type )
+  {
+    case MSG_FATAL:   fprintf( logfile, "%s: FATAL: ",   program ); break;
+    case MSG_ERROR:   fprintf( logfile, "%s: ERROR: ",   program ); break;
+    case MSG_WARNING: fprintf( logfile, "%s: WARNING: ", program ); break;
+    case MSG_NOTICE:  fprintf( logfile, "%s: NOTE: ",    program ); break;
+    case MSG_INFO:    fprintf( logfile, "%s: INFO: ",    program ); break;
+    case MSG_DEBUG:   fprintf( logfile, "%s: DEBUG: ",   program ); break;
+    case MSG_LOG:
+      fprintf( logfile, "%s: ", program );
+      break;
+    default:
+      fprintf( logfile, "%s: ", program );
+      break;
+  }
+  va_start( argp, format );
+  vfprintf( errlog, format, argp );
+  fprintf( errlog, "\n" );
+  (void)fflush( errlog );
+}
diff --git a/cscmd/msglog.h b/cscmd/msglog.h
new file mode 100644
index 0000000..fc256f0
--- /dev/null
+++ b/cscmd/msglog.h
@@ -0,0 +1,98 @@
+
+/**********************************************************************
+
+  Copyright 2019 Andrey V.Kosteltsev
+
+  Licensed under the Radix.pro License, Version 1.0 (the "License");
+  you may not use this file  except  in compliance with the License.
+  You may obtain a copy of the License at
+
+     https://radix.pro/licenses/LICENSE-1.0-en_US.txt
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+  implied.
+
+ **********************************************************************/
+
+#ifndef _MSG_LOG_H_
+#define _MSG_LOG_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern FILE *errlog;
+
+extern void (*fatal_error_hook)( void );
+
+extern char *program;
+extern int   exit_status;
+
+enum _msg_type
+{
+  MSG_FATAL = 0,
+  MSG_ERROR,
+  MSG_WARNING,
+  MSG_NOTICE,
+  MSG_INFO,
+  MSG_DEBUG,
+
+  MSG_LOG
+};
+
+#define FATAL_ERROR( ... )                    \
+  do                                          \
+  {                                           \
+    logmsg( errlog, MSG_FATAL, __VA_ARGS__ ); \
+    if( fatal_error_hook) fatal_error_hook(); \
+    exit( EXIT_FAILURE );                     \
+  } while (0)
+
+#define ERROR( ... )                          \
+  do                                          \
+  {                                           \
+    logmsg( errlog, MSG_ERROR, __VA_ARGS__ ); \
+    ++exit_status;                            \
+  } while (0)
+
+#define WARNING( ... )                          \
+  do                                            \
+  {                                             \
+    logmsg( errlog, MSG_WARNING, __VA_ARGS__ ); \
+  } while (0)
+
+#define NOTICE( ... )                          \
+  do                                           \
+  {                                            \
+    logmsg( errlog, MSG_NOTICE, __VA_ARGS__ ); \
+  } while (0)
+
+#define INFO( ... )                          \
+  do                                         \
+  {                                          \
+    logmsg( errlog, MSG_INFO, __VA_ARGS__ ); \
+  } while (0)
+
+#define DEBUG( ... )                          \
+  do                                          \
+  {                                           \
+    logmsg( errlog, MSG_DEBUG, __VA_ARGS__ ); \
+  } while (0)
+
+#define LOG( ... )                          \
+  do                                        \
+  {                                         \
+    logmsg( errlog, MSG_LOG, __VA_ARGS__ ); \
+  } while (0)
+
+
+extern void logmsg( FILE *logfile, enum _msg_type type, char *format, ... );
+
+
+#ifdef __cplusplus
+}  /* ... extern "C" */
+#endif
+
+#endif /* _MSG_LOG_H_ */
diff --git a/cscmd/parse.y b/cscmd/parse.y
new file mode 100644
index 0000000..cdb2d5b
--- /dev/null
+++ b/cscmd/parse.y
@@ -0,0 +1,107 @@
+
+%{
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdarg.h>
+#include <limits.h>
+#include <locale.h>
+#include <wchar.h>
+#include <wctype.h>
+
+#include <defs.h>
+
+#include <main.h>
+#include <error.h>
+#include <msglog.h>
+#include <xalloc.h>
+#include <utf8ing.h>
+#include <symtab.h>
+#include <parse.h>
+#include <lex.h>
+
+
+%}
+
+
+%union
+{
+  SYMBOL *sym;
+}
+
+%token <sym>  VARIABLE 501 SECTION 502 REPO 503
+%token <sym>  NUMERICAL 510 STRING 511 PATH 512
+%right '='
+%left UNARYMINUS
+/************************************************************
+  Following tokens declared only for verbose error messaging
+  to prevent "$undefined" values of unexpected symbols:
+ */
+%token '!' '"' '#' '$' '%' '&' '\'' '(' ')' '*' '/' '+' '-'
+%token '.' ',' ':' '<' '>' '?' '@' '[' '\\' ']' '^' '`'
+
+%start list
+
+%%
+list:    /* nothing */
+       | list ';'
+       | list repo
+       | list section
+       | list assign ';'
+       | list error  ';' { return 1; }
+       ;
+
+assign:  VARIABLE '=' NUMERICAL  { (void)assign_value( $1, $3 ); }
+       | VARIABLE '=' '+' NUMERICAL { (void)assign_value( $1, $4 ); }
+       | VARIABLE '=' '-' NUMERICAL %prec UNARYMINUS { $4->u.value = -$4->u.value; (void)assign_value( $1, $4 ); }
+       | VARIABLE '=' STRING     { (void)assign_value( $1, $3 ); }
+       | VARIABLE '=' PATH       { (void)assign_value( $1, $3 ); }
+       | NUMERICAL '=' NUMERICAL { (void)assign_value( $1, $3 ); }
+       | STRING '=' STRING       { (void)assign_value( $1, $3 ); }
+       | PATH '=' PATH           { (void)assign_value( $1, $3 ); }
+       ;
+
+alist:   /* nothing */
+       | alist ';'
+       | alist assign ';'
+       ;
+
+repo:    REPO PATH '{'
+           {
+             if( lookup_repo( $2->u.path ) )
+             {
+               error( "Repository '%s' is already defined", $2->u.path );
+               return 1;
+             }
+             (void)assign_value( $1, $2 ); push_symlist( (SYMBOL **)&($1->list) );
+           }
+           alist
+         '}' { pop_symlist(); }
+       ;
+
+rlist:   /* nothing */
+       | rlist repo
+       ;
+
+section:
+         SECTION STRING '{'
+           {
+             if( lookup_section( $2->u.string ) )
+             {
+               error( "Section '%s' is already defined", $2->u.string );
+               return 1;
+             }
+             (void)assign_value( $1, $2 ); push_symlist( (SYMBOL **)&($1->list) );
+           }
+           rlist
+         '}' { pop_symlist(); }
+       ;
+
+%%
+
diff --git a/cscmd/rc.cscmd.in b/cscmd/rc.cscmd.in
new file mode 100644
index 0000000..e3297e1
--- /dev/null
+++ b/cscmd/rc.cscmd.in
@@ -0,0 +1,96 @@
+#!/bin/sh
+#
+# /etc/rc.d/rc.@CSCM_PROGRAM@d - @CSCM_PROGRAM_NAME@ daemon control script.
+#
+
+BIN=@sbindir@/@PROGRAM_DAEMON@
+CONF=@CSCM_CONFIG@
+BCF=@CSCM_HOME_PATH@/@CSCM_PROGRAM@/@CSCM_PROGRAM@.bcf
+PID=@CSCM_PID_DIR@/@CSCM_PROGRAM@d.pid
+LOG=@CSCM_LOG_DIR@/@CSCM_PROGRAM@d.log
+
+INOTIFY=--inotify
+
+cscmd_start() {
+  # Sanity checks.
+  if [ ! -r $CONF ]; then
+    echo "$CONF does not appear to exist. Abort."
+    exit 1
+  fi
+
+  if [ -s $PID ]; then
+    echo "@CSCM_PROGRAM_NAME@ daemon appears to already be running?"
+    exit 1
+  fi
+
+  echo "Starting @CSCM_PROGRAM_NAME@ server daemon..."
+  if [ -x $BIN ]; then
+    $BIN --daemonize $INOTIFY --scm=@CSCM_NAME@ --pid=$PID --log=$LOG --bcf=$BCF --config=$CONF
+  fi
+}
+
+cscmd_test_conf() {
+  echo "Checking configuration for correct syntax and then"
+  echo "trying to open files referenced in configuration..."
+  echo ""
+  if [ -s $PID ] ; then
+    echo "@PROGRAM_DAEMON@: $CONF: Config file is correct."
+  else
+    $BIN --test --scm=@CSCM_NAME@ --pid=$PID --log=$LOG --bcf=$BCF --config=$CONF
+  fi
+}
+
+cscmd_status() {
+  if [ -s $PID ] ; then
+    echo "@CSCM_PROGRAM_NAME@ daemon is running as PID: $(cat $PID)"
+  else
+    echo "@CSCM_PROGRAM_NAME@ daemon is stopped."
+  fi
+}
+
+cscmd_stop() {
+  echo "Shutdown @CSCM_PROGRAM_NAME@ daemon gracefully..."
+  if [ -s $PID ] ; then
+    kill -TERM $(cat $PID)
+  else
+    echo "@CSCM_PROGRAM_NAME@ daemon appears to already be stopped."
+  fi
+}
+
+cscmd_reload() {
+  echo "Reloading @CSCM_PROGRAM_NAME@ daemon configuration..."
+  if [ -s $PID ] ; then
+    kill -HUP $(cat $PID)
+  else
+    echo "@CSCM_PROGRAM_NAME@ daemon is not running."
+  fi
+}
+
+cscmd_restart() {
+  cscmd_stop
+  sleep 3
+  cscmd_start
+}
+
+case "$1" in
+  check)
+    cscmd_test_conf
+    ;;
+  reload)
+    cscmd_reload
+    ;;
+  restart)
+    cscmd_restart
+    ;;
+  start)
+    cscmd_start
+    ;;
+  stop)
+    cscmd_stop
+    ;;
+  status)
+    cscmd_status
+    ;;
+  *)
+  echo "usage: `basename $0` {check|reload|restart|start|stop|status}"
+esac
diff --git a/cscmd/symtab.c b/cscmd/symtab.c
new file mode 100644
index 0000000..62899c1
--- /dev/null
+++ b/cscmd/symtab.c
@@ -0,0 +1,471 @@
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <unistd.h>
+
+#include <defs.h>
+
+#include <main.h>
+#include <error.h>
+#include <msglog.h>
+#include <xalloc.h>
+#include <utf8ing.h>
+#include <lex.h>
+
+#include <symtab.h>
+#include <parse.h>
+
+
+SYMBOL *symlist = NULL;
+
+static SYMTAB *symtab  = NULL;
+
+static int constants_counter = 0;
+static int sections_counter  = 0;
+static int repos_counter     = 0;
+
+
+static SYMBOL *free_const( SYMBOL *sp )
+{
+  SYMBOL *next = NULL;
+
+  if( !sp ) return next;
+
+  next = sp->next;
+
+  free( sp->name );
+
+  switch( sp->type )
+  {
+    case STRING:
+      if( sp->u.string ) free( sp->u.string );
+      break;
+    case PATH:
+      if( sp->u.string ) free( sp->u.path );
+      break;
+
+    case NUMERICAL:
+    default:
+      break;
+  }
+
+  free( sp );
+
+  return next;
+}
+
+static void free_symlist( SYMBOL *sp );
+
+static SYMBOL *free_symbol( SYMBOL *sp )
+{
+  SYMBOL *next = NULL;
+
+  if( !sp ) return next;
+
+  if( sp->list ) (void)free_symlist( sp->list );
+
+  next = sp->next;
+
+  free( sp->name );
+
+  switch( sp->type )
+  {
+    case SECTION:
+    case STRING:
+      if( sp->u.string ) free( sp->u.string );
+      break;
+    case REPO:
+    case PATH:
+      if( sp->u.string ) free( sp->u.path );
+      break;
+
+    case VARIABLE:
+    case NUMERICAL:
+    default:
+      break;
+  }
+
+  free( sp );
+
+  return next;
+}
+
+static void free_symlist( SYMBOL *sp )
+{
+  SYMBOL *next = NULL;
+
+  if( !sp ) return;
+
+  next = free_symbol( sp );
+  while( next )
+  {
+    next = free_symbol( next );
+  }
+}
+
+/******************************************
+  Initialize the stak of symlist pointers:
+ */
+void init_symtab( void )
+{
+  SYMTAB *sa = (SYMTAB *)xmalloc( sizeof( SYMTAB ) );
+
+  symtab  = NULL;
+  symlist = NULL;
+
+  constants_counter = 0;
+  sections_counter  = 0;
+  repos_counter     = 0;
+
+  sa->symlist = (SYMBOL **)&symlist;
+  sa->next = symtab;
+  symtab = sa;
+}
+
+
+/*******************************************
+  Push the address of symlist to the stack:
+ */
+void push_symlist( SYMBOL **head )
+{
+  if( head )
+  {
+    SYMTAB  *sa = (SYMTAB *)xmalloc( sizeof( SYMTAB ) );
+
+    sa->symlist = head;
+    sa->next = symtab;
+    symtab = sa;
+  }
+}
+
+/********************************************
+  Pop the address of symlist from the stack:
+ */
+void pop_symlist( void )
+{
+  if( symtab && symtab->next )
+  {
+    SYMTAB *sa = symtab;
+    symtab = symtab->next;
+    free( sa );
+  }
+}
+
+/************************************
+  Free the stak of symlist pointers:
+ */
+void fini_symtab( void )
+{
+  if( !symtab ) return;
+
+  while( symtab )
+  {
+    SYMTAB *sa = symtab;
+    symtab = symtab->next;
+    free( sa );
+  }
+
+  constants_counter = 0;
+  sections_counter  = 0;
+  repos_counter     = 0;
+
+  symtab  = NULL;
+  free_symlist( symlist ); /* free main symlist */
+  symlist = NULL;
+}
+
+
+/******************************
+  Reverse symlist recursively:
+ */
+void reverse_symlist( SYMBOL **head )
+{
+  SYMBOL *prev = NULL, *curr = *head, *next;
+
+  while( curr )
+  {
+    if( curr->list ) reverse_symlist( (SYMBOL **)&(curr->list) );
+
+    next = curr->next;
+    curr->next = prev;
+    prev = curr;
+    curr = next;
+  }
+
+  *head = prev;
+}
+
+/******************************************************
+  Remove temporary constants from symlist recursively:
+ */
+void remove_consts( SYMBOL **head )
+{
+  SYMBOL *tmp = NULL;
+
+  while( *head )
+  {
+    tmp = *head;
+    if( !strncmp( tmp->name, "__const.", 8 ) )
+    {
+      *head = tmp->next;
+      (void)free_const( tmp );
+    }
+    else
+    {
+      head = &tmp->next;
+      if( tmp->list ) remove_consts( (SYMBOL **)&(tmp->list) );
+    }
+  }
+}
+
+
+SYMBOL *assign_value( SYMBOL *dest, SYMBOL *src )
+{
+  SYMBOL *ret = NULL;
+
+  if( !dest || !src ) return ret;
+
+  if( dest->type == VARIABLE ) /* always not initialized */
+  {
+    dest->type = src->type;
+    dest->u.value = 0;
+
+    switch( src->type )
+    {
+      case NUMERICAL:
+        dest->u.value = src->u.value;
+        break;
+      case STRING:
+        dest->u.string = strdup( (const char *)src->u.string );
+        break;
+      case PATH:
+        dest->u.path = strdup( (const char *)src->u.path );
+        break;
+      default:
+        /* error */
+        break;
+    }
+  }
+  else if( dest->type == STRING || dest->type == SECTION )
+  {
+    switch( src->type )
+    {
+      case STRING:
+        if( src->u.string )
+        {
+          if( dest->u.string ) free( dest->u.string );
+          dest->u.string = strdup( (const char *)src->u.string );
+        }
+        else
+        {
+          if( dest->u.string ) free( dest->u.string );
+          dest->u.string = NULL;
+        }
+        break;
+      default:
+        /* error */
+        break;
+    }
+  }
+  else if( dest->type == PATH || dest->type == REPO )
+  {
+    switch( src->type )
+    {
+      case PATH:
+        if( src->u.path )
+        {
+          if( dest->u.path ) free( dest->u.path );
+          dest->u.path = strdup( (const char *)src->u.path );
+        }
+        else
+        {
+          if( dest->u.path ) free( dest->u.path );
+          dest->u.path = NULL;
+        }
+        break;
+      default:
+        /* error */
+        break;
+    }
+  }
+  else if( dest->type == src->type )
+  {
+    switch( src->type )
+    {
+      case NUMERICAL:
+        dest->u.value = src->u.value;
+        break;
+      case STRING:
+        if( dest->u.string ) free( dest->u.string );
+        dest->u.string = strdup( (const char *)src->u.string );
+        break;
+      case PATH:
+        if( dest->u.path ) free( dest->u.path );
+        dest->u.path = strdup( (const char *)src->u.path );
+        break;
+      default:
+        /* error */
+        break;
+    }
+  }
+  else
+  {
+    /* error */
+  }
+
+  return dest;
+}
+
+
+SYMBOL *install( const char *s, int type, ... )
+{
+  SYMBOL *sp       = NULL;
+  char    name[80] = "__undef";
+
+  if( !symtab ) return sp;
+
+  va_list  argp;
+
+  if( ! type ) return( sp );
+
+  sp = (SYMBOL *)xmalloc( sizeof( SYMBOL ) );
+
+  switch( type )
+  {
+    case NUMERICAL:
+    case STRING:
+    case PATH:
+      sprintf( (char *)&name[0], "__const.%d", constants_counter++ );
+      sp->name = strdup( (const char *)&name[0] );
+      break;
+    case REPO:
+      sprintf( (char *)&name[0], "__repo.%d", repos_counter++ );
+      sp->name = strdup( (const char *)&name[0] );
+      break;
+    case SECTION:
+      sprintf( (char *)&name[0], "__section.%d", sections_counter++ );
+      sp->name = strdup( (const char *)&name[0] );
+      break;
+    default:
+      if( !s )
+        sp->name = strdup( (const char *)&name[0] );
+      else
+        sp->name = strdup( s );
+      break;
+  }
+  sp->type  = type;
+
+  va_start( argp, type );
+
+  switch( type )
+  {
+    case SECTION:
+    case STRING:
+    {
+      char *string = (char *)va_arg( argp, char * );
+      if( string ) sp->u.string = strdup( (const char *)string );
+      break;
+    }
+    case REPO:
+    case PATH:
+    {
+      char *path = (char *)va_arg( argp, char * );
+      if( path ) sp->u.path = strdup( (const char *)path );
+      break;
+    }
+
+    case VARIABLE:
+    case NUMERICAL:
+    default:
+      sp->u.value = (int)va_arg( argp, int );
+      break;
+  }
+
+  sp->next  = *(symtab->symlist);  /* alloc in begin of list */
+  *(symtab->symlist) = sp;
+
+  return( sp );
+}
+
+/***********************************
+  Find variable in current symlist:
+ */
+SYMBOL *lookup( const char *s )
+{
+  SYMBOL *sp;
+
+  for( sp = *(symtab->symlist); sp != (SYMBOL *)0; sp = sp->next )
+  {
+    if( strcmp( sp->name, s ) == 0 ) return( sp );
+  }
+
+  return( 0 );  /* запись не найдена */
+}
+
+/*********************************
+  Find section in global symlist:
+ */
+SYMBOL *lookup_section( const char *s )
+{
+  SYMBOL *sp;
+
+  for( sp = symlist; sp != (SYMBOL *)0; sp = sp->next )
+  {
+    if( sp->type == SECTION && sp->u.string && strcmp( sp->u.string, s ) == 0 ) return( sp );
+  }
+
+  return( 0 );  /* запись не найдена */
+}
+
+/********************************
+  Find repo globally in symlist:
+ */
+#if 0
+SYMBOL *lookup_repo_global( const char *s )
+{
+  SYMBOL *sp;
+
+  for( sp = symlist; sp != (SYMBOL *)0; sp = sp->next )
+  {
+    /***************************
+      lookup in global section:
+     */
+    if( sp->type == REPO && sp->u.path && strcmp( sp->u.path, s ) == 0 ) return( sp );
+
+    /*************************
+      lookup in each section:
+     */
+    if( sp->type == SECTION && sp->list )
+    {
+      SYMBOL *rp;
+      for( rp = sp->list; rp != (SYMBOL *)0; rp = rp->next )
+      {
+        if( rp->type == REPO && rp->u.path && strcmp( rp->u.path, s ) == 0 ) return( rp );
+      }
+    }
+  }
+
+  return( 0 );  /* запись не найдена */
+}
+#endif
+
+/*******************************
+  Find repo in current symlist:
+ */
+SYMBOL *lookup_repo( const char *s )
+{
+  SYMBOL *sp;
+
+  for( sp = *(symtab->symlist); sp != (SYMBOL *)0; sp = sp->next )
+  {
+    if( sp->type == REPO && sp->u.path && strcmp( sp->u.path, s ) == 0 ) return( sp );
+  }
+
+  return( 0 );  /* запись не найдена */
+}
diff --git a/cscmd/symtab.h b/cscmd/symtab.h
new file mode 100644
index 0000000..5019569
--- /dev/null
+++ b/cscmd/symtab.h
@@ -0,0 +1,67 @@
+
+#ifndef    __SYMTAB_H
+#define    __SYMTAB_H
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/******************************
+  SYMBOL is a node of symlist:
+ */
+typedef struct symbol SYMBOL;
+struct symbol
+{
+  char *name;          /* Variable name */
+  int   type;          /* VARIABLE, SECTION, REPO, NUMERICAL, STRING, PATH */
+  union
+  {
+    int   value;       /* for NUMERICAL */
+    char *string;      /* for STRING */
+    char *path;        /* for PATH */
+  } u;
+
+  struct symbol *list; /* The list of variables. Used for SECTION and REPO */
+
+  struct symbol *next; /* Next Symbol */
+};
+
+/**********************************************
+  SYMTAB is an entry of the stack of symlists:
+ */
+typedef struct symtab SYMTAB;
+struct symtab
+{
+  SYMBOL **symlist;
+  struct symtab *next; /* Next Entry */
+};
+
+
+extern SYMBOL *symlist;
+
+extern void init_symtab( void );
+extern void push_symlist( SYMBOL **head );
+extern void pop_symlist( void );
+extern void fini_symtab( void );
+
+extern void reverse_symlist( SYMBOL **head );
+extern void remove_consts( SYMBOL **head );
+
+//debug
+extern void print_symlist( int indent, SYMBOL *head );
+
+extern SYMBOL *install( const char *s, int type, ... );
+extern SYMBOL *lookup( const char *s );
+extern SYMBOL *lookup_section( const char *s );
+extern SYMBOL *lookup_repo( const char *s );
+
+extern SYMBOL *assign_value( SYMBOL *dest, SYMBOL *src );
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  /* __SYMTAB_H */
diff --git a/cscmd/utf8ing.c b/cscmd/utf8ing.c
new file mode 100644
index 0000000..1d67c79
--- /dev/null
+++ b/cscmd/utf8ing.c
@@ -0,0 +1,121 @@
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdarg.h>
+#include <limits.h>
+#include <locale.h>
+#include <wchar.h>
+#include <wctype.h>
+
+#include <defs.h>
+#include <utf8ing.h>
+
+
+static const ucs4_t replacement_char     = 0xfffd;
+static const ucs4_t maximum_ucs4         = 0x7fffffff;
+
+static const int    half_shift           = 10;
+static const ucs4_t half_base            = 0x0010000;
+
+static const ucs4_t surrogate_high_start = 0xd800;
+static const ucs4_t surrogate_high_end   = 0xdbff;
+static const ucs4_t surrogate_low_start  = 0xdc00;
+static const ucs4_t surrogate_low_end    = 0xdfff;
+
+static utf8_t
+first_byte_mark[7] = { 0x00, 0x00, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc };
+
+
+/***************************************************************
+  static copy_ucs4_to_utf8()
+
+     Переводит строку символов UCS4( src ) в UTF8( dest ).
+
+     Возвращаемое значение:
+        Количество байт, реально записанное в DEST.
+
+     NOTE:
+        Выход за пределы памяти, выделенной под указатель DEST
+        не контролируются.
+        Подразумевается, что строка SRC имеет null-терминатор.
+ ***************************************************************/
+int copy_ucs4_to_utf8( utf8_t *dest, const ucs4_t *src )
+{
+  utf8_t   target[7];
+  utf8_t  *ptr;
+  int      count = 0;
+
+  while( *src )
+  {
+    ucs4_t        c;
+    int           bytes_to_write = 0;
+    const ucs4_t  byte_mask = 0xbf;
+    const ucs4_t  byte_mark = 0x80;
+
+    c = *src++;
+
+    if( c >= surrogate_high_start &&
+        c <= surrogate_high_end   && *src )
+    {
+      ucs4_t c2 = *src;
+
+      if( c2 >= surrogate_low_start &&
+          c2 <= surrogate_low_end      )
+      {
+        c = ((c  - surrogate_high_start) << half_shift) +
+             (c2 - surrogate_low_start) + half_base;
+        ++src;
+      }
+    }
+
+         if( c <          0x80 ) bytes_to_write = 1;
+    else if( c <         0x800 ) bytes_to_write = 2;
+    else if( c <       0x10000 ) bytes_to_write = 3;
+    else if( c <      0x200000 ) bytes_to_write = 4;
+    else if( c <     0x4000000 ) bytes_to_write = 5;
+    else if( c <= maximum_ucs4 ) bytes_to_write = 6;
+    else
+    {
+      bytes_to_write = 2;   c = replacement_char;
+    }
+
+    ptr = &target[0] + bytes_to_write;
+
+    switch( bytes_to_write )
+    {
+      case 6:
+        *--ptr = (c | byte_mark) & byte_mask; c >>= 6;
+      case 5:
+        *--ptr = (c | byte_mark) & byte_mask; c >>= 6;
+      case 4:
+        *--ptr = (c | byte_mark) & byte_mask; c >>= 6;
+      case 3:
+        *--ptr = (c | byte_mark) & byte_mask; c >>= 6;
+      case 2:
+        *--ptr = (c | byte_mark) & byte_mask; c >>= 6;
+      case 1:
+        *--ptr = c | first_byte_mark[bytes_to_write];
+    }
+
+    ptr = &target[0];
+
+    while( bytes_to_write > 0 )
+    {
+      *dest++ = *ptr++; /* write byte */
+      --bytes_to_write;
+      ++count;
+    }
+
+  } /* End while( *src ) */
+
+  *dest = (utf8_t)0; /* null terminator */
+
+  return( count );
+
+} /* End of static copy_ucs4_to_utf8() */
diff --git a/cscmd/utf8ing.h b/cscmd/utf8ing.h
new file mode 100644
index 0000000..d96cda8
--- /dev/null
+++ b/cscmd/utf8ing.h
@@ -0,0 +1,22 @@
+
+#ifndef    __UTF8_H
+#define    __UTF8_H
+
+
+typedef unsigned int   ucs4_t;
+typedef unsigned char  utf8_t;
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+extern int copy_ucs4_to_utf8( utf8_t *, const ucs4_t * );
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  /* __UTF8_H */
diff --git a/cscmd/xalloc.c b/cscmd/xalloc.c
new file mode 100644
index 0000000..80f2581
--- /dev/null
+++ b/cscmd/xalloc.c
@@ -0,0 +1,36 @@
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <defs.h>
+
+#include <error.h>
+#include <msglog.h>
+
+
+void *xmalloc( size_t n )
+{
+  void *p = NULL;
+
+  p = malloc( n );
+  if( !p ) no_space();
+  bzero( p, n );
+
+  return( p );
+}
+
+void *xrealloc( void *b, size_t n )
+{
+  void *p = NULL;
+
+  p = realloc( b , n );
+  if( !p ) no_space();
+
+  return( p );
+}
diff --git a/cscmd/xalloc.h b/cscmd/xalloc.h
new file mode 100644
index 0000000..3c9691b
--- /dev/null
+++ b/cscmd/xalloc.h
@@ -0,0 +1,19 @@
+
+#ifndef    __XALLOC_H
+#define    __XALLOC_H
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+extern void *xmalloc  ( size_t );
+extern void *xrealloc ( void *, size_t );
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  /* __XALLOC_H */
diff --git a/defs.h b/defs.h
new file mode 100644
index 0000000..8a69be9
--- /dev/null
+++ b/defs.h
@@ -0,0 +1,24 @@
+
+#ifndef    __DEFS_H
+#define    __DEFS_H
+
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  /* __DEFS_H */
diff --git a/doc/build-packages/archlinux/PKGBUILD b/doc/build-packages/archlinux/PKGBUILD
new file mode 100644
index 0000000..15218b2
--- /dev/null
+++ b/doc/build-packages/archlinux/PKGBUILD
@@ -0,0 +1,49 @@
+#
+# Maintainer: Andrey V.Kosteltsev <kx@radix.pro>
+#
+pkgname=cscm
+pkgver=0.1.4
+pkgrel=2
+pkgdesc='cScm Configuration Daemon for cSvn-ui and cGit-ui packages'
+arch=('x86_64')
+url='https://csvn.radix.pro'
+license=('custom')
+depends=('pcre2')
+source=("https://ftp.radix.pro/pub/cscm/${pkgname}-${pkgver}.tar.xz")
+md5sums=('..Check MD5 sum before Building Package..')
+
+build() {
+  cd ${pkgname}-${pkgver}
+
+  CFLAGS="-O2 -fPIC -Wno-unused-result" \
+  ./configure \
+    --prefix=/usr \
+    --sysconfdir=/etc \
+    --libdir=/usr/lib \
+    --sbindir=/usr/bin \
+    --with-controldir=/etc/rc.d \
+    --with-logrotatedir=/etc/logrotate.d \
+    --with-homepath=/var/lib \
+    --with-logdir=/var/log \
+    --with-piddir=/var/run
+  make
+}
+
+package() {
+  cd ${pkgname}-${pkgver}
+
+  make install DESTDIR="${pkgdir}"
+
+  # Install systemd unit:
+  install -d ${pkgdir}/usr/lib/systemd/system
+  install -m 644 doc/build-packages/rpms/csvnd.service ${pkgdir}/usr/lib/systemd/system
+  install -m 644 doc/build-packages/rpms/cgitd.service ${pkgdir}/usr/lib/systemd/system
+  rm -rf ${pkgdir}/etc/rc.d
+
+  # Gsip man pages:
+  gzip -9 ${pkgdir}/usr/share/man/man8/cscmd.8
+
+  # Install documentation:
+  install -d ${pkgdir}/usr/share/doc/${pkgname}-${pkgver}
+  cp -a LICENSE README README.md doc/ ${pkgdir}/usr/share/doc/${pkgname}-${pkgver}
+}
diff --git a/doc/build-packages/archlinux/README b/doc/build-packages/archlinux/README
new file mode 100644
index 0000000..0ffa946
--- /dev/null
+++ b/doc/build-packages/archlinux/README
@@ -0,0 +1,32 @@
+
+Sytem requires:
+  fakeroot, logrotate
+
+Build time requires:
+  make, binutils, gcc, bison, autoconf, automake, pkgconfig,
+  pcre2
+
+Runtime requires:
+  pcre2
+
+As non-privileged user:
+
+  $ mkdir build
+  $ cp PKGBUILD build/
+  $ cd build/
+  $ makepkg
+
+As root:
+
+  # pacman -U ./cscm-0.1.4-2-x86_64.pkg.tar.zst
+
+After install binary package we have to create /etc/csvn-ui.rc [or|and] /etc/cgit-ui.rc
+configuration files and then enable and run csvnd.service [or|and] cgitd.service:
+
+ $ systemctl enable csvnd.service
+ $ systemctl start csvnd.service
+
+ $ systemctl enable cgitd.service
+ $ systemctl start cgitd.service
+
+Then we can to enable csvn-ui [or|and] cgit-ui web-servers.
diff --git a/doc/build-packages/rpms/README b/doc/build-packages/rpms/README
new file mode 100644
index 0000000..e8c6c5d
--- /dev/null
+++ b/doc/build-packages/rpms/README
@@ -0,0 +1,26 @@
+
+To build RPM packages we have to copy source package downloaded
+from https://ftp.radix.pro/pub/cscm directory to /usr/src/packages/SOURSES/
+directory. And also copy the cscm.spec file into /usr/src/packages/SPECS/
+directory. Then edit the package version in the cscm.spec file according
+to source package version.
+
+Dependencies: pcre2-devel.
+
+RPMs can be built by following command:
+
+ $ rpmbuild --define "_topdir /usr/src/packages" -ba /usr/src/packages/SPECS/cscm.spec
+
+Resulting RPMs will be saved in /usr/src/packages/SRPMS/ and
+/usr/src/packages/RPMS/`uname -m`/ directories.
+
+After install binary RPM we have to create /etc/csvn-ui.rc [or|and] /etc/cgit-ui.rc
+configuration files and then enable and run csvnd.service [or|and] cgitd.service:
+
+ $ systemctl enable csvnd.service
+ $ systemctl start csvnd.service
+
+ $ systemctl enable cgitd.service
+ $ systemctl start cgitd.service
+
+Then we can to enable csvn-ui [or|and] cgit-ui web-servers.
diff --git a/doc/build-packages/rpms/cgitd.service b/doc/build-packages/rpms/cgitd.service
new file mode 100644
index 0000000..fa87546
--- /dev/null
+++ b/doc/build-packages/rpms/cgitd.service
@@ -0,0 +1,13 @@
+
+[Unit]
+Description=The cGit daemon
+After=network.target
+
+[Service]
+PIDFile=/var/run/cgitd.pid
+ExecStart=/usr/sbin/cscmd --daemonize --inotify --scm=git --pid=/var/run/cgitd.pid --log=/var/log/cgitd.log --config=/etc/cgit-ui.rc
+ExecReload=/bin/kill -s HUP $MAINPID
+ExecStop=/bin/kill -s TERM $MAINPID
+
+[Install]
+WantedBy=multi-user.target
diff --git a/doc/build-packages/rpms/cscm.spec b/doc/build-packages/rpms/cscm.spec
new file mode 100644
index 0000000..3ce0248
--- /dev/null
+++ b/doc/build-packages/rpms/cscm.spec
@@ -0,0 +1,57 @@
+
+Name:       cscm
+Version:    0.1.4
+Release:    2
+Summary:    cScm Configuration Daemon
+License:    RADIX-1.0
+Group:      System/base
+Source:     https://ftp.radix.pro/pub/cscm/cscm-0.1.4.tar.xz
+Url:        https://csvn.radix/pro/cscm/
+
+
+%define _sysconfdir /etc
+%define _libdir     /usr/lib64
+%define _homepath   /var/lib
+%define _logdir     /var/log
+%define _piddir     /var/run
+%define _systemddir /usr/lib/systemd/system
+
+
+%description
+cScm - is a Configuration Daemon for cSvn-ui and cGit-ui packages
+
+%prep
+%setup -q
+
+%build
+./configure \
+  --prefix=/usr \
+  --sysconfdir=%{_sysconfdir} \
+  --libdir=%{_libdir} \
+  --with-controldir=%{_sysconfdir}/rc.d \
+  --with-logrotatedir=%{_sysconfdir}/logrotate.d \
+  --with-homepath=%{_homepath} \
+  --with-logdir=%{_logdir} \
+  --with-piddir=%{_piddir}
+
+%install
+make install DESTDIR=%{buildroot}
+install -d %{buildroot}%{_systemddir}
+install -m 644 doc/build-packages/rpms/csvnd.service %{buildroot}%{_systemddir}
+install -m 644 doc/build-packages/rpms/cgitd.service %{buildroot}%{_systemddir}
+rm -rf %{buildroot}%{_sysconfdir}/rc.d
+
+%clean
+%{?buildroot:%__rm -rf "%{buildroot}"}
+
+%files
+%defattr(-,root,root)
+%{_sysconfdir}/*
+%attr(755,root,root) %{_sbindir}/cscmd
+%{_homepath}/*
+%{_includedir}/*
+%{_datadir}/*
+%doc LICENSE README README.md doc/
+%{_systemddir}/csvnd.service
+%{_systemddir}/cgitd.service
+
diff --git a/doc/build-packages/rpms/csvnd.service b/doc/build-packages/rpms/csvnd.service
new file mode 100644
index 0000000..f51919c
--- /dev/null
+++ b/doc/build-packages/rpms/csvnd.service
@@ -0,0 +1,13 @@
+
+[Unit]
+Description=The cSvn daemon
+After=network.target
+
+[Service]
+PIDFile=/var/run/csvnd.pid
+ExecStart=/usr/sbin/cscmd --daemonize --inotify --scm=svn --pid=/var/run/csvnd.pid --log=/var/log/csvnd.log --config=/etc/csvn-ui.rc
+ExecReload=/bin/kill -s HUP $MAINPID
+ExecStop=/bin/kill -s TERM $MAINPID
+
+[Install]
+WantedBy=multi-user.target
diff --git a/doc/build-packages/slackware/README b/doc/build-packages/slackware/README
new file mode 100644
index 0000000..b9a2999
--- /dev/null
+++ b/doc/build-packages/slackware/README
@@ -0,0 +1,6 @@
+
+cscm (a SCM Configuration Daemon)
+
+cScm is a SCM Configuration daemon for cSvn-ui and cGit-ui packages.
+
+Homepage: https://csvn.radix.pro
diff --git a/doc/build-packages/slackware/cscm.SlackBuild b/doc/build-packages/slackware/cscm.SlackBuild
new file mode 100644
index 0000000..110edc0
--- /dev/null
+++ b/doc/build-packages/slackware/cscm.SlackBuild
@@ -0,0 +1,92 @@
+#!/bin/bash
+
+cd $(dirname $0) ; CWD=$(pwd)
+
+PKGNAM=cscm
+VERSION=${VERSION:-$(echo cscm-*.tar.?z* | rev | cut -f 3- -d . | cut -f 1 -d - | rev)}
+BUILD=${BUILD:-2}
+
+# Automatically determine the architecture we're building on:
+if [ -z "$ARCH" ]; then
+  case "$( uname -m )" in
+    i?86) export ARCH=i586 ;;
+    arm*) export ARCH=arm ;;
+    # Unless $ARCH is already set, use uname -m for all other archs:
+       *) export ARCH=$( uname -m ) ;;
+  esac
+fi
+
+if [ "$ARCH" = "i586" ]; then
+  SLKCFLAGS="-O2 -march=i586 -mtune=i686"
+  LIBDIRSUFFIX=""
+elif [ "$ARCH" = "s390" ]; then
+  SLKCFLAGS="-O2"
+  LIBDIRSUFFIX=""
+elif [ "$ARCH" = "x86_64" ]; then
+  SLKCFLAGS="-O2 -fPIC"
+  LIBDIRSUFFIX="64"
+else
+  SLKCFLAGS="-O2"
+  LIBDIRSUFFIX=""
+fi
+
+TMP=${TMP:-/tmp}
+PKG=$TMP/package-${PKGNAM}
+
+rm -rf $PKG
+mkdir -p $TMP $PKG
+
+cd $TMP
+rm -rf ${PKGNAM}-${VERSION}
+tar xvf $CWD/${PKGNAM}-${VERSION}.tar.?z* || exit 1
+cd ${PKGNAM}-$VERSION || exit 1
+
+chown -R root:root .
+find . \
+ \( -perm 777 -o -perm 775 -o -perm 711 -o -perm 555 -o -perm 511 \) \
+ -exec chmod 755 {} \+ -o \
+ \( -perm 666 -o -perm 664 -o -perm 600 -o -perm 444 -o -perm 440 -o -perm 400 \) \
+ -exec chmod 644 {} \+
+
+CFLAGS="$SLKCFLAGS" \
+./configure \
+  --prefix=/usr \
+  --sysconfdir=/etc \
+  --libdir=/usr/lib${LIBDIRSUFFIX} \
+  --with-controldir=/etc/rc.d \
+  --with-logrotatedir=/etc/logrotate.d \
+  --with-homepath=/var/lib \
+  --with-logdir=/var/log \
+  --with-piddir=/var/run \
+  --build=$ARCH-slackware-linux || exit 1
+
+
+make || exit 1
+make install DESTDIR=$PKG || exit 1
+
+mv $PKG/etc/rc.d/rc.csvnd $PKG/etc/rc.d/rc.csvnd.new
+mv $PKG/etc/rc.d/rc.cgitd $PKG/etc/rc.d/rc.cgitd.new
+
+find $PKG | xargs file | grep -e "executable" -e "shared object" | grep ELF \
+  | cut -f 1 -d : | xargs strip --strip-unneeded 2> /dev/null
+
+mv $PKG/usr/share/man $PKG/usr/
+gzip -9 $PKG/usr/man/man?/*
+rmdir $PKG/usr/share/man
+
+find $PKG/usr/man -type f -exec gzip -9 {} \;
+
+mkdir -p $PKG/usr/doc/${PKGNAM}-${VERSION}
+cp -a \
+  LICENSE README README.md doc \
+  $PKG/usr/doc/${PKGNAM}-${VERSION}
+
+cp -a $CWD/${PKGNAM}.info        $PKG/usr/doc/${PKGNAM}-${VERSION}
+cp -a $CWD/${PKGNAM}.SlackBuild  $PKG/usr/doc/${PKGNAM}-${VERSION}
+
+mkdir -p $PKG/install
+cat $CWD/doinst.sh  > $PKG/install/doinst.sh
+cat $CWD/slack-desc > $PKG/install/slack-desc
+
+cd $PKG
+/sbin/makepkg -l y -c n $TMP/${PKGNAM}-${VERSION}-${ARCH}-${BUILD}.txz
diff --git a/doc/build-packages/slackware/cscm.info b/doc/build-packages/slackware/cscm.info
new file mode 100644
index 0000000..9436a98
--- /dev/null
+++ b/doc/build-packages/slackware/cscm.info
@@ -0,0 +1,10 @@
+PRGNAM="cscm"
+VERSION="0.1.4"
+HOMEPAGE="http://csvn.radix.pro"
+DOWNLOAD="https://ftp.radix.pro/pub/cscm/cscm-0.1.4.tar.xz"
+MD5SUM="..Check MD5 sum before Building Package.."
+DOWNLOAD_x86_64=""
+MD5SUM_x86_64=""
+REQUIRES="libpcre2-32"
+MAINTAINER="Andrey V.Kosteltsev"
+EMAIL="kx@radix.pro"
diff --git a/doc/build-packages/slackware/doinst.sh b/doc/build-packages/slackware/doinst.sh
new file mode 100644
index 0000000..7ee79cf
--- /dev/null
+++ b/doc/build-packages/slackware/doinst.sh
@@ -0,0 +1,15 @@
+
+config() {
+  NEW="$1"
+  OLD="`dirname $NEW`/`basename $NEW .new`"
+  # If there's no config file by that name, mv it over:
+  if [ ! -r $OLD ]; then
+    mv $NEW $OLD
+  elif [ "`cat $OLD | md5sum`" = "`cat $NEW | md5sum`" ]; then # toss the redundant copy
+    rm $NEW
+  fi
+  # Otherwise, we leave the .new copy for the admin to consider...
+}
+
+config etc/rc.d/rc.csvnd.new
+config etc/rc.d/rc.cgitd.new
diff --git a/doc/build-packages/slackware/slack-desc b/doc/build-packages/slackware/slack-desc
new file mode 100644
index 0000000..2d7c2b9
--- /dev/null
+++ b/doc/build-packages/slackware/slack-desc
@@ -0,0 +1,19 @@
+# HOW TO EDIT THIS FILE:
+# The "handy ruler" below makes it easier to edit a package description. Line
+# up the first '|' above the ':' following the base package name, and the '|' on
+# the right side marks the last column you can put a character in. You must make
+# exactly 11 lines for the formatting to be correct. It's also customary to
+# leave one space after the ':'.
+
+    |-----handy-ruler------------------------------------------------------|
+cscm: cscm (a SCM Configuration Daemon)
+cscm:
+cscm: cSvn is a SCM Configuration daemon for cSvn-ui and cGit-ui packages.
+cscm:
+cscm:
+cscm:
+cscm: Homepage: https://csvn.radix.pro
+cscm:
+cscm:
+cscm:
+cscm:
diff --git a/doc/cscmd.8.md b/doc/cscmd.8.md
new file mode 100644
index 0000000..b0c9423
--- /dev/null
+++ b/doc/cscmd.8.md
@@ -0,0 +1,112 @@
+
+# [cScm Daemon](https://csvn.radix.pro/cscm/trunk/doc/cscmd.8.md)
+
+**cscmd** &#8211; a daemon for monitoring changes in SCM configuration files.
+
+
+## Table of Contents
+
+* [Options](#options)
+* [Grammar](#grammar)
+* [See Also](#see-also)
+
+<br/>
+
+In order to not load the **cSvn-ui** and **cGit-ui** CGI scripts with unnecessary functionality and speed up
+its work, information about the list of repositories is transmitted to it in binary form through
+shared memory (see: */dev/shm/csvn.bcf*, */dev/shm/cgit.bcf*).
+
+Binary Config File (**BCF**) format is similar to the simplified implementation of the **COFF**
+and is described in the header file [*/usr/include/cscm/bcf.h*](https://csvn.radix.pro/cscm/trunk/cscm/bcf.h).
+
+On startup, the daemon parses the */etc/csvn-ui.rc* or */etc/csvn-ui.rc* configuration files
+(depends on **--scm=[svn|git]** option) and stores its binary forms in shared memory. In addition, the binary
+form is saved in the daemon's home directory, by default it is the file */var/lib/csvn/csvn.bcf* or
+*/var/lib/cgit/cgit.bcf*.
+
+**cscmd(8)** daemon rereads SCM configuration files upon arrival of the **-HUP** signal and, in addition,
+can be configured to monitor changes in the configuration files using the `--inotify` option. If the `--inotify`
+option is specified, then after editing the file */ets/csvn-ui.rc* or */ets/cgit-ui.rc* and saving it to disk,
+the **cscmd** daemon will reread file */etc/csvn-ui.rc* or */etc/cgit-ui.rc* as if it received the **-HUP** signal.
+
+The work of the **cscmd(8)** daemon's can be monitored by the log, which it leaves in the file
+*/var/log/csvnd.log* or the file */var/log/cgitd.log* (depends on **--scm=[svn|git]** option). There you can also
+observe messages about errors that are detected in */ets/csvn-ui.rc* or */ets/cgit-ui.rc* files, if any.
+
+To start the **cScm** daemon during system boot, can be used the start/stop script */etc/rc.d/rc.csvnd* or
+*/etc/rc.d/rc.cgitd*.
+
+
+## Options
+
+
+### -h, --help
+
+Display help information.
+
+
+### -v, --version
+
+Display the version of **cScm** daemon.
+
+
+### -d, --daemonize
+
+Run in background as a daemon.
+
+
+### -i, --inotify
+
+Notify about configuration changes. If this option is set then **cscmd(8)** daemon selects changes
+made in */etc/csvn-ui.rc* or */etc/cgit-ui.rc* configuration file and reread configuration when changes
+is done. Without this option rereading configuration file can be done by sending **-HUP** to the **cscmd(8)**
+process.
+
+
+### -b, --bcf=<BCF_FILE>
+
+Binary config file (depends on **--scm=[svn|git]** option). Default: */var/lib/csvn/csvn.bcf*.
+
+
+### -c, --config=<CONFIG_FILE>
+
+Config file (depends on **--scm=[svn|git]** option). Default: */etc/csvn-ui.rc*.
+
+
+### -l, --log=<LOG_FILE>
+
+Log file (depends on **--scm=[svn|git]** option). Default: */var/log/csvnd.log*.
+
+
+### -p, --pid=<PID_FILE>
+
+Log file (depends on **--scm=[svn|git]** option). Default: */var/run/csvnd.pid*.
+
+
+### -s, --scm=[svn|git]
+
+SCM engine name: **svn** or **git**. Default: **svn**.
+
+
+### -t, --test
+Test the config file and exit.
+
+
+## Grammar
+
+The grammar of the language describing the **cScm** congfiguration is simple
+([**parse.y**](https://csvn.radix.pro/cscm/trunk/cscmd/parse.y)). File */etc/csvn-ui.rc*
+allows you to set variable values and create repository lists as named structures.
+
+Any variables can be defined in the */etc/csvn-ui.rc* file, but only variables with
+reserved names will be used by the **cSvn** or **cGit** CGI script.
+
+In addition, if a variable already defined at the global level is specified in the
+description of a repository, then within this repository this variable will have a local
+value. In other words, within the repository, global variables can be overridden.
+
+
+## See Also
+
+> [**README**](https://csvn.radix.pro/cscm/trunk/README.md)
+