Radix cross Linux Package Tools

Package Tools – is a set of utilities to create, install, and update RcL packages

8 Commits   0 Branches   2 Tags
Index: install-pkglist.c
===================================================================
--- install-pkglist.c	(nonexistent)
+++ install-pkglist.c	(revision 5)
@@ -0,0 +1,2033 @@
+
+/**********************************************************************
+
+  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.
+
+ **********************************************************************/
+
+#include <config.h>
+
+#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> /* flock(2)    */
+#include <fcntl.h>
+#include <linux/limits.h>
+#include <alloca.h>   /* alloca(3)   */
+#include <string.h>   /* strdup(3)   */
+#include <strings.h>  /* index(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 <pthread.h>
+
+#include <math.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 <msglog.h>
+#include <wrapper.h>
+#include <system.h>
+#include <dlist.h>
+
+#if defined( HAVE_DIALOG )
+#include <dialog-ui.h>
+#endif
+
+#define PROGRAM_NAME "install-pkglist"
+
+#include <defs.h>
+
+#define WAIT_USEC_FOR_CHILD 10000
+
+char *program = PROGRAM_NAME;
+char *root = NULL, *srcdir = NULL, *pkglist_fname = NULL,
+     *tmpdir = NULL, *curdir = NULL;
+
+int   rqck = 0, gpgck = 0, progress = 0, parallel = 0, error_pkgs_list = 0, ncpus = 0;
+
+int   exit_status = EXIT_SUCCESS; /* errors counter */
+char *selfdir     = NULL;
+
+int __done = 0, __child = 0, __terminated = 0, __successful = 0, __all = 0;
+
+enum _install_mode {
+  CONSOLE = 0,
+  INFODIALOG,
+  MENUDIALOG
+} install_mode = CONSOLE;
+
+enum _input_type {
+  IFMT_PKG = 0,
+  IFMT_LOG,
+
+  IFMT_UNKNOWN
+};
+
+/*********************************************
+  Package structures and declarations:
+ */
+enum _procedure
+{
+  INSTALL = 0, /* 'install' */
+  UPDATE       /* 'update'  */
+};
+
+enum _priority
+{
+  REQUIRED = 0, /* synonims: REQUIRED    | required    | REQ | req */
+  RECOMMENDED,  /* synonims: RECOMMENDED | recommended | REC | rec */
+  OPTIONAL,     /* synonims: OPTIONAL    | optional    | OPT | opt */
+  SKIP          /* synonims: SKIP        | skip        | SKP | skp */
+};
+
+struct pkg
+{
+  char *group;
+  char *name;
+  char *version;
+};
+
+struct package
+{
+  char  *name;
+  char  *version;
+  char  *group;
+  char  *description;
+
+  char  *tarball;
+
+  enum  _procedure procedure; /* install procedure     */
+  enum  _priority  priority;  /* install user priority */
+};
+
+enum _priority install_priority = OPTIONAL; /* by default allow all packages exept 'SKIP' */
+
+struct dlist *requires = NULL; /* list of pkg structs     */
+struct dlist *packages = NULL; /* list of package structs */
+
+static void free_requires( void );
+static void free_packages( void );
+
+/*
+  End of package structures and declarations.
+ *********************************************/
+
+/*********************************************
+  Return status declarations:
+ */
+struct pkgrc
+{
+  pid_t  pid;
+  int    status;
+  char  *name;
+  char  *version;
+  char  *group;
+};
+
+struct dlist *pkgrcl = NULL; /* list of pkgrc structs */
+
+static void          free_pkgrcl( void );
+static struct pkgrc *find_pkgrc( struct dlist *list, pid_t pid );
+/*
+  End of return status declarations.
+ *********************************************/
+
+
+void free_resources()
+{
+  if( root )           { free( root );           root           = NULL; }
+  if( srcdir )         { free( srcdir );         srcdir         = NULL; }
+  if( pkglist_fname )  { free( pkglist_fname );  pkglist_fname  = NULL; }
+
+  if( requires ) free_requires();
+  if( packages ) free_packages();
+  if( pkgrcl )   free_pkgrcl();
+
+  if( curdir )         { free( curdir );         curdir         = NULL; }
+  if( selfdir )        { free( selfdir );        selfdir        = NULL; }
+}
+
+void usage()
+{
+  free_resources();
+
+  fprintf( stdout, "\n" );
+  fprintf( stdout, "Usage: %s [options] <pkglist>\n", program );
+  fprintf( stdout, "\n" );
+  fprintf( stdout, "Create Packages List in the installation order from set of PKGLOGs\n" );
+  fprintf( stdout, "or  set of PACKAGEs placed in the source directory.  If the source\n" );
+  fprintf( stdout, "directory is not defined then directory of <pkglist>  will be used\n" );
+  fprintf( stdout, "as source PACKAGE directory.\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, "  -c,--check-requires           Check the list of requires before install.\n" );
+  fprintf( stdout, "  -g,--gpg-verify               Verify GPG2 signature. The signature must be\n" );
+  fprintf( stdout, "                                saved in a file whose name is the same as the\n" );
+  fprintf( stdout, "                                package file name, but with the extension '.asc'\n" );
+  fprintf( stdout, "                                and located in the same directory as the package.\n" );
+#if defined( HAVE_DIALOG )
+  fprintf( stdout, "  -i,--info-dialog              Show package description during install\n" );
+  fprintf( stdout, "                                process using ncurses dialog.\n" );
+  fprintf( stdout, "  -m,--menu-dialog              Ask for confirmation the inatallation.\n" );
+#endif
+  fprintf( stdout, "  --parallel                    Parallel installation (dangerous; required the\n" );
+  fprintf( stdout, "                                checking of DB integrity after installation).\n" );
+  fprintf( stdout, "  --errlist                     Print the list of not installed packages to the\n" );
+  fprintf( stdout, "                                stderr in following format:\n" );
+  fprintf( stdout, "                                    group/name:version:status\n" );
+  fprintf( stdout, "                                This option is applicable only for parallel\n" );
+  fprintf( stdout, "                                installations.\n" );
+  fprintf( stdout, "  --progress                    Show progress bar instead of packages information.\n" );
+
+  fprintf( stdout, "  -p,--priority=<required|recommended|optional|all>\n" );
+  fprintf( stdout, "                                Рackage priorities allowed for installation:\n" );
+  fprintf( stdout, "                                - optional | all ) install all packages;\n" );
+  fprintf( stdout, "                                - recommended )    install required and recommended;\n" );
+  fprintf( stdout, "                                - required )       install only required packages.\n" );
+
+  fprintf( stdout, "  -r,--root=<DIR>               Target rootfs path.\n" );
+
+  fprintf( stdout, "  -s,--source=<DIR>             Packages source directory.\n" );
+
+  fprintf( stdout, "\n" );
+  fprintf( stdout, "Parameter:\n" );
+  fprintf( stdout, "  <DIR|pkglist>                 Input PKGLIST file name or a source\n"  );
+  fprintf( stdout, "                                directory to find default PKGLIST.\n"  );
+  fprintf( stdout, "\n" );
+  fprintf( stdout, "If sourse packages directory is defined by option -s,--source then\n" );
+  fprintf( stdout, "specified <DIR|pkglist> argumet will be considered relative to the\n" );
+  fprintf( stdout, "source directory.\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) 2019 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 );
+}
+
+
+static void remove_trailing_slash( char *dir )
+{
+  char *s;
+
+  if( !dir || dir[0] == '\0' ) return;
+
+  s = dir + strlen( dir ) - 1;
+  while( *s == '/' )
+  {
+    *s = '\0'; --s;
+  }
+}
+
+
+static int _mkdir_p( const char *dir, const mode_t mode )
+{
+  char  *buf;
+  char  *p = NULL;
+  struct stat sb;
+
+  if( !dir ) return -1;
+
+  buf = (char *)alloca( strlen( dir ) + 1 );
+  strcpy( buf, dir );
+
+  remove_trailing_slash( buf );
+
+  /* check if path exists and is a directory */
+  if( stat( buf, &sb ) == 0 )
+  {
+    if( S_ISDIR(sb.st_mode) )
+    {
+      return 0;
+    }
+  }
+
+  /* mkdir -p */
+  for( p = buf + 1; *p; ++p )
+  {
+    if( *p == '/' )
+    {
+      *p = 0;
+      /* test path */
+      if( stat( buf, &sb ) != 0 )
+      {
+        /* path does not exist - create directory */
+        if( mkdir( buf, mode ) < 0 )
+        {
+          return -1;
+        }
+      } else if( !S_ISDIR(sb.st_mode) )
+      {
+        /* not a directory */
+        return -1;
+      }
+      *p = '/';
+    }
+  }
+
+  /* test path */
+  if( stat( buf, &sb ) != 0 )
+  {
+    /* path does not exist - create directory */
+    if( mkdir( buf, mode ) < 0 )
+    {
+      return -1;
+    }
+  } else if( !S_ISDIR(sb.st_mode) )
+  {
+    /* not a directory */
+    return -1;
+  }
+
+  return 0;
+}
+
+
+static void _rm_tmpdir( const char *dirpath )
+{
+  DIR    *dir;
+  char   *path;
+  size_t  len;
+
+  struct stat    path_sb, entry_sb;
+  struct dirent *entry;
+
+  if( stat( dirpath, &path_sb ) == -1 )
+  {
+    return; /* stat returns error code; errno is set */
+  }
+
+  if( S_ISDIR(path_sb.st_mode) == 0 )
+  {
+    return; /* dirpath is not a directory */
+  }
+
+  if( (dir = opendir(dirpath) ) == NULL )
+  {
+    return; /* Cannot open direcroty; errno is set */
+  }
+
+  len = strlen( dirpath );
+
+  while( (entry = readdir( dir )) != NULL)
+  {
+
+    /* skip entries '.' and '..' */
+    if( ! strcmp( entry->d_name, "." ) || ! strcmp( entry->d_name, ".." ) ) continue;
+
+    /* determinate a full name of an entry */
+    path = alloca( len + strlen( entry->d_name ) + 2 );
+    strcpy( path, dirpath );
+    strcat( path, "/" );
+    strcat( path, entry->d_name );
+
+    if( stat( path, &entry_sb ) == 0 )
+    {
+      if( S_ISDIR(entry_sb.st_mode) )
+      {
+        /* recursively remove a nested directory */
+        _rm_tmpdir( path );
+      }
+      else
+      {
+        /* remove a file object */
+        (void)unlink( path );
+      }
+    }
+    /* else { stat() returns error code; errno is set; and we have to continue the loop } */
+
+  }
+
+  /* remove the devastated directory and close the object of this directory */
+  (void)rmdir( dirpath );
+
+  closedir( dir );
+}
+
+
+static char *_mk_tmpdir( void )
+{
+  char   *buf = NULL, *p, *tmp = "/tmp";
+  size_t  len = 0, size = 0;
+
+  (void)umask( S_IWGRP | S_IWOTH ); /* octal 022 */
+
+  /* Get preferred directory for tmp files */
+  if( (p = getenv( "TMP" )) != NULL ) {
+    tmp = p;
+  }
+  else if( (p = getenv( "TEMP" )) != NULL ) {
+    tmp = p;
+  }
+
+  size = strlen( tmp ) + strlen( DISTRO_NAME ) + strlen( program ) + 12;
+
+  buf = (char *)malloc( size );
+  if( !buf ) return NULL;
+
+  len = snprintf( buf, size, (const char *)"%s/%s/%s-%.7u", tmp, DISTRO_NAME, program, getpid() );
+  if( len == 0 || len == size - 1 )
+  {
+    free( buf ); return NULL;
+  }
+
+  _rm_tmpdir( (const char *)&buf[0] );
+
+  if( _mkdir_p( buf, S_IRWXU | S_IRWXG | S_IRWXO ) == 0 )
+  {
+    return buf;
+  }
+
+  free( buf ); return NULL;
+}
+
+
+
+void fatal_error_actions( void )
+{
+  logmsg( errlog, MSG_NOTICE, "Free resources on FATAL error..." );
+  if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); }
+  free_resources();
+}
+
+void sigint( int signum )
+{
+  (void)signum;
+
+  if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); }
+  free_resources();
+}
+
+void sigchld( int signum )
+{
+  pid_t  pid = 0;
+  int    status;
+
+  (void)signum;
+
+  while( (pid = waitpid( -1, &status, WNOHANG )) > 0 )
+  {
+    struct pkgrc *pkgrc = find_pkgrc( pkgrcl, pid );
+
+    ++__terminated; /* One of children with 'pid' is terminated */
+
+    if( WIFEXITED( status ) )
+    {
+      if( (int)WEXITSTATUS( status ) > 0 )
+      {
+        ++exit_status; /* printf( "Child %d returned non zero status: %d\n", pid, (int)WEXITSTATUS (status) ); */
+        if( pkgrc ) pkgrc->status = (int)WEXITSTATUS (status);
+      }
+      else
+      {
+        ++__successful; /* printf( "Child %d terminated with status: %d\n", pid, (int)WEXITSTATUS (status) ); */
+        if( pkgrc ) pkgrc->status = (int)WEXITSTATUS (status);
+      }
+    }
+    else if( WIFSIGNALED( status ) )
+    {
+      ++exit_status; /* printf( "Child %d terminated on signal: %d\n", pid, WTERMSIG( status ) ); */
+      if( pkgrc ) pkgrc->status = 253;
+    }
+    else
+    {
+      ++exit_status; /* printf( "Child %d terminated on unknown reason\n", pid ); */
+      if( pkgrc ) pkgrc->status = 254;
+    }
+
+  }
+
+  if( pid == -1 && errno == ECHILD )
+  {
+    /* No child processes: */
+    __done = 1;
+  }
+  return;
+}
+
+
+static void set_signal_handlers()
+{
+  struct sigaction  sa;
+  sigset_t          set;
+
+  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 );
+
+  /* System V fork+wait does not work if SIGCHLD is ignored */
+  memset( &sa, 0, sizeof( sa ) );
+  sa.sa_handler = sigchld;         /* CHLD */
+  sa.sa_flags = SA_RESTART;
+  sigemptyset( &set );
+  sigaddset( &set, SIGCHLD );
+  sa.sa_mask = set;
+  sigaction( SIGCHLD, &sa, NULL );
+
+  memset( &sa, 0, sizeof( sa ) );  /* ignore SIGPIPE */
+  sa.sa_handler = SIG_IGN;
+  sa.sa_flags = 0;
+  sigaction( SIGPIPE, &sa, NULL );
+}
+
+
+static enum _input_type check_package_file( char *uncompress, const char *fname )
+{
+  struct stat st;
+  size_t pkglog_size = 0;
+  unsigned char buf[8];
+  int rc, fd;
+
+  /* SIGNATURES: https://www.garykessler.net/library/file_sigs.html */
+
+  if( uncompress )
+  {
+    *uncompress = '\0';
+  }
+
+  if( stat( fname, &st ) == -1 )
+  {
+    FATAL_ERROR( "Cannot access %s file: %s", basename( (char *)fname ), strerror( errno ) );
+  }
+
+  pkglog_size = st.st_size;
+
+  if( (fd = open( fname, O_RDONLY )) == -1 )
+  {
+    FATAL_ERROR( "Cannot open %s file: %s", basename( (char *)fname ), strerror( errno ) );
+  }
+
+  rc = (int)read( fd, (void *)&buf[0], 7 );
+  if( rc != 7 )
+  {
+    close( fd ); return IFMT_UNKNOWN;
+  }
+  buf[7] = '\0';
+
+  /* TEXT */
+  if( !strncmp( (const char *)&buf[0], "PACKAGE", 7 ) )
+  {
+    close( fd ); return IFMT_LOG;
+  }
+
+  /* GZ */
+  if( buf[0] == 0x1F && buf[1] == 0x8B && buf[2] == 0x08 )
+  {
+    if( uncompress ) { *uncompress = 'x'; }
+    close( fd ); return IFMT_PKG;
+  }
+
+  /* BZ2 */
+  if( buf[0] == 0x42 && buf[1] == 0x5A && buf[2] == 0x68 )
+  {
+    if( uncompress ) { *uncompress = 'j'; }
+    close( fd ); return IFMT_PKG;
+  }
+
+  /* XZ */
+  if( buf[0] == 0xFD && buf[1] == 0x37 && buf[2] == 0x7A &&
+      buf[3] == 0x58 && buf[4] == 0x5A && buf[5] == 0x00   )
+  {
+    if( uncompress ) { *uncompress = 'J'; }
+    close( fd ); return IFMT_PKG;
+  }
+
+  if( pkglog_size > 262 )
+  {
+    if( lseek( fd, 257, SEEK_SET ) == -1 )
+    {
+      FATAL_ERROR( "Cannot check signature of %s file: %s", basename( (char *)fname ), strerror( errno ) );
+    }
+    rc = (int)read( fd, &buf[0], 5 );
+    if( rc != 5 )
+    {
+      FATAL_ERROR( "Cannot read signature of %s file", basename( (char *)fname ) );
+    }
+    /* TAR */
+    if( buf[0] == 0x75 && buf[1] == 0x73 && buf[2] == 0x74 && buf[3] == 0x61 && buf[4] == 0x72 )
+    {
+      close( fd ); return IFMT_PKG;
+    }
+  }
+
+  close( fd ); return IFMT_UNKNOWN;
+}
+
+
+void get_args( int argc, char *argv[] )
+{
+#if defined( HAVE_DIALOG )
+  const char* short_options = "hvcgimp:r:s:";
+#else
+  const char* short_options = "hvcgp:r:s:";
+#endif
+
+#define PROGRESS 812
+#define PARALLEL 872
+#define _ERRLIST 873
+
+  const struct option long_options[] =
+  {
+    { "help",           no_argument,       NULL, 'h' },
+    { "version",        no_argument,       NULL, 'v' },
+    { "check-requires", no_argument,       NULL, 'c' },
+    { "gpg-verify",     no_argument,       NULL, 'g' },
+#if defined( HAVE_DIALOG )
+    { "info-dialog",    no_argument,       NULL, 'i' },
+    { "menu-dialog",    no_argument,       NULL, 'm' },
+#endif
+    { "parallel",       no_argument,       NULL, PARALLEL },
+    { "errlist",        no_argument,       NULL, _ERRLIST },
+    { "progress",       no_argument,       NULL, PROGRESS },
+    { "priority",       required_argument, NULL, 'p' },
+    { "root",           required_argument, NULL, 'r' },
+    { "source",         required_argument, NULL, 's' },
+    { 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 'c':
+      {
+        rqck = 1;
+        break;
+      }
+      case 'g':
+      {
+        gpgck = 1;
+        break;
+      }
+
+#if defined( HAVE_DIALOG )
+      case 'i':
+      {
+        install_mode = INFODIALOG;
+        break;
+      }
+      case 'm':
+      {
+        install_mode = MENUDIALOG;
+        break;
+      }
+#endif
+
+      case PARALLEL:
+      {
+        parallel = 1;
+        break;
+      }
+      case _ERRLIST:
+      {
+        error_pkgs_list = 1;
+        break;
+      }
+      case PROGRESS:
+      {
+        progress = 1;
+        break;
+      }
+
+      case 'p':
+      {
+        if( optarg != NULL )
+        {
+          char *match = NULL;
+
+          if( strlen( (const char *)optarg ) > 2 )
+          {
+            to_lowercase( optarg );
+            if( (match = strstr( optarg, "req" )) && match == optarg ) {
+              install_priority = REQUIRED;
+            }
+            else if( (match = strstr( optarg, "rec" )) && match == optarg ) {
+              install_priority = RECOMMENDED;
+            }
+            else if( (match = strstr( optarg, "opt" )) && match == optarg ) {
+              install_priority = OPTIONAL;
+            }
+            else if( (match = strstr( optarg, "all" )) && match == optarg ) {
+              install_priority = OPTIONAL;
+            }
+            else {
+              FATAL_ERROR( "Unknown --priority '%s' value", optarg );
+            }
+          }
+          else
+          {
+            FATAL_ERROR( "Unknown --priority '%s' value", optarg );
+          }
+        }
+        else
+          /* option is present but without value */
+          usage();
+        break;
+      }
+
+      case 'r':
+      {
+        if( optarg != NULL )
+        {
+          char cwd[PATH_MAX];
+
+          bzero( (void *)cwd, PATH_MAX );
+          if( optarg[0] != '/' && curdir )
+          {
+            /* skip current directory definition './' at start of argument: */
+            if( !strncmp( optarg, "./", 2 ) && strncmp( optarg, "..", 2 ) )
+              (void)sprintf( cwd, "%s/%s", curdir, optarg + 2 );
+            else if( (strlen( optarg ) == 1) && !strncmp( optarg, ".", 1 ) )
+              (void)sprintf( cwd, "%s", curdir );
+            else
+              (void)sprintf( cwd, "%s/%s", curdir, optarg );
+            root = xstrdup( (const char *)cwd );
+          }
+          else
+          {
+            root = xstrdup( (const char *)optarg );
+          }
+          remove_trailing_slash( root );
+        }
+        else
+          /* option is present but without value */
+          usage();
+        break;
+      }
+
+      case 's':
+      {
+        if( optarg != NULL )
+        {
+          char cwd[PATH_MAX];
+
+          bzero( (void *)cwd, PATH_MAX );
+          if( optarg[0] != '/' && curdir )
+          {
+            (void)sprintf( cwd, "%s/%s", curdir, optarg );
+            srcdir = xstrdup( (const char *)cwd );
+          }
+          else
+          {
+            srcdir = xstrdup( (const char *)optarg );
+          }
+          remove_trailing_slash( srcdir );
+        }
+        else
+          /* option is present but without value */
+          usage();
+        break;
+      }
+
+      case '?': default:
+      {
+        usage();
+        break;
+      }
+    }
+  }
+
+
+  /* last command line argument is the intput PKGLIST file */
+  if( optind < argc )
+  {
+    struct stat st;
+    char  *buf = NULL;
+
+    bzero( (void *)&st, sizeof( struct stat ) );
+
+    buf = (char *)malloc( (size_t)PATH_MAX );
+    if( !buf ) { FATAL_ERROR( "Cannot allocate memory" ); }
+    bzero( (void *)buf, PATH_MAX );
+
+    (void)strcpy( buf, (const char *)argv[optind++] );
+    remove_trailing_slash( (char *)&buf[0] );
+
+    if( srcdir)
+    {
+      char *tmp = xstrdup( (const char *)&buf[0] );
+
+      /* Ignore already defined srcdir if absolute path is specified: */
+      if( buf[0] != '/' )
+      {
+        if( !strncmp( tmp, "./", 2 ) && strncmp( tmp, "..", 2 ) )
+          (void)sprintf( buf, "%s/%s", srcdir, tmp + 2 );
+        else if( (strlen( tmp ) == 1) && !strncmp( tmp, ".", 1 ) )
+          (void)sprintf( buf, "%s", srcdir );
+        else
+          (void)sprintf( buf, "%s/%s", srcdir, tmp );
+      }
+      free( tmp );
+
+      free( srcdir ); srcdir = xstrdup( (const char *)buf );
+    }
+    else
+    {
+      char *tmp = xstrdup( (const char *)&buf[0] );
+
+      if( buf[0] != '/' && curdir )
+      {
+        if( !strncmp( tmp, "./", 2 ) && strncmp( tmp, "..", 2 ) )
+          (void)sprintf( buf, "%s/%s", curdir, tmp + 2 );
+        else if( (strlen( tmp ) == 1) && !strncmp( tmp, ".", 1 ) )
+          (void)sprintf( buf, "%s", curdir );
+        else
+          (void)sprintf( buf, "%s/%s", curdir, tmp );
+      }
+      free( tmp );
+
+      srcdir = xstrdup( (const char *)buf );
+    }
+
+    stat( (const char *)&buf[0], &st ); /* Do not check return status */
+
+    if( S_ISDIR(st.st_mode) )
+    {
+      /**********************************************************
+        Add default '.pkglist' file name to the source dir name:
+       */
+      (void)sprintf( buf, "%s/.pkglist", srcdir );
+      pkglist_fname = xstrdup( (const char *)buf );
+    }
+    else
+    {
+      if( S_ISREG(st.st_mode) )
+      {
+        pkglist_fname = xstrdup( (const char *)buf );
+        free( srcdir ); srcdir = xstrdup( (const char *)dirname( (char *)&buf[0] ) );
+      }
+      else
+      {
+        FATAL_ERROR( "Specified '%s' PKGLIST is not a regular file", basename( (char *)&buf[0] ) );
+      }
+    }
+
+    free( buf );
+  }
+  else
+  {
+    usage();
+  }
+
+  /*********************************************
+    root is always have the trailing slash '/':
+   */
+  {
+    struct stat st;
+    char  *buf = NULL;
+    int    len;
+
+    bzero( (void *)&st, sizeof( struct stat ) );
+
+    buf = (char *)malloc( (size_t)PATH_MAX );
+    if( !buf ) { FATAL_ERROR( "Cannot allocate memory" ); }
+    bzero( (void *)buf, PATH_MAX );
+
+    if( !root )
+    {
+      buf[0] = '/'; buf[1] = '\0';
+      root = xstrdup( (const char *)buf );
+    }
+    else
+    {
+      len = strlen( root );
+
+      (void)strcpy( buf, (const char *)root );
+      if( buf[ len - 1 ] != '/' )
+      {
+        buf[len] = '/'; buf[len+1] = '\0';
+        free( root ); root = xstrdup( (const char *)buf );
+      }
+    }
+
+    if( stat( (const char *)&buf[0], &st ) == -1 )
+    {
+      FATAL_ERROR( "Cannot access '%s' file or directory: %s", buf, strerror( errno ) );
+    }
+    if( !S_ISDIR(st.st_mode) )
+    {
+      FATAL_ERROR( "Defined --root '%s' is not a directory", buf );
+    }
+
+    free( buf );
+  }
+  /*
+    End of set root path routine.
+   *********************************************/
+}
+
+
+
+
+/*********************************************
+  Package functions:
+ */
+static char *strprio( enum _priority priority, int short_name )
+{
+  char *p = NULL;
+
+  switch( priority )
+  {
+    case REQUIRED:
+      p = ( short_name ) ? "REQ" : "REQUIRED";
+      break;
+    case RECOMMENDED:
+      p = ( short_name ) ? "REC" : "RECOMMENDED";
+      break;
+    case OPTIONAL:
+      p = ( short_name ) ? "OPT" : "OPTIONAL";
+      break;
+    case SKIP:
+      p = ( short_name ) ? "SKP" : "SKIP";
+      break;
+  }
+  return p;
+}
+
+static char *strproc( enum _procedure procedure )
+{
+  char *p = NULL;
+
+  switch( procedure )
+  {
+    case INSTALL:
+      p = "install";
+      break;
+    case UPDATE:
+      p = "update";
+      break;
+  }
+  return p;
+}
+
+
+static struct pkg *pkg_alloc( void )
+{
+  struct pkg *pkg = NULL;
+
+  pkg = (struct pkg *)malloc( sizeof( struct pkg ) );
+  if( !pkg ) { FATAL_ERROR( "Cannot allocate memory" ); }
+  bzero( (void *)pkg, sizeof( struct pkg ) );
+
+  return pkg;
+}
+
+static void pkg_free( struct pkg *pkg )
+{
+  if( pkg )
+  {
+    if( pkg->name )    { free( pkg->name );    pkg->name    = NULL; }
+    if( pkg->version ) { free( pkg->version ); pkg->version = NULL; }
+    if( pkg->group )   { free( pkg->group );   pkg->group   = NULL; }
+
+    free( pkg );
+  }
+}
+
+static void __pkg_free_func( void *data, void *user_data )
+{
+  struct pkg *pkg = (struct pkg *)data;
+  if( pkg ) { pkg_free( pkg ); }
+}
+
+static void free_requires( void )
+{
+  if( requires ) { dlist_free( requires, __pkg_free_func ); requires = NULL; }
+}
+
+static void add_required( struct pkg *pkg )
+{
+  requires = dlist_append( requires, (void *)pkg );
+}
+
+///////////////////// only if we deside to print requires list
+//static void _print_requires( void *data, void *user_data )
+//{
+//  struct pkg *pkg = (struct pkg *)data;
+//
+//  if( pkg )
+//  {
+//    if( pkg->group )
+//      fprintf( stderr, "%s/%s:%s\n", pkg->group, pkg->name, pkg->version );
+//    else
+//      fprintf( stderr, "%s:%s\n", pkg->name, pkg->version );
+//  }
+//}
+//
+//static void print_requires( void )
+//{
+//  dlist_foreach( requires, _print_requires, NULL );
+//}
+/////////////////////
+
+static struct package *package_alloc( void )
+{
+  struct package *package = NULL;
+
+  package = (struct package *)malloc( sizeof( struct package ) );
+  if( !package ) { FATAL_ERROR( "Cannot allocate memory" ); }
+  bzero( (void *)package, sizeof( struct package ) );
+
+  return package;
+}
+
+static void package_free( struct package *package )
+{
+  if( package )
+  {
+    if( package->name )    { free( package->name );    package->name    = NULL; }
+    if( package->version ) { free( package->version ); package->version = NULL; }
+    if( package->group )   { free( package->group );   package->group   = NULL; }
+
+    if( package->description ) { free( package->description ); package->description   = NULL; }
+    if( package->tarball )     { free( package->tarball );     package->tarball   = NULL; }
+
+    free( package );
+  }
+}
+
+static void __package_free_func( void *data, void *user_data )
+{
+  struct package *package = (struct package *)data;
+  if( package ) { package_free( package ); }
+}
+
+static void free_packages( void )
+{
+  if( packages ) { dlist_free( packages, __package_free_func ); packages = NULL; }
+}
+
+static void add_package( struct package *package )
+{
+  packages = dlist_append( packages, (void *)package );
+}
+
+////////////////////////////// just for testing
+//static void _print_packages( void *data, void *user_data )
+//{
+//  struct package *package = (struct package *)data;
+//
+//  if( package )
+//  {
+//    if( package->group )
+//      fprintf( stderr, "%s/%s:%s:%s:%s:%s:%s\n", package->group, package->name, package->version, strproc( package->procedure ), strprio( package->priority, 0 ),
+//                                                 package->description, package->tarball );
+//    else
+//      fprintf( stderr, "%s:%s:%s:%s:%s:%s\n", package->name, package->version, strproc( package->procedure ), strprio( package->priority, 0 ),
+//                                              package->description, package->tarball );
+//  }
+//}
+//
+//static void print_packages( void )
+//{
+//  dlist_foreach( packages, _print_packages, NULL );
+//}
+//////////////////////////////
+
+/*
+  End of package functions.
+ *********************************************/
+
+
+/*********************************************
+  Return status functions:
+ */
+static struct pkgrc *pkgrc_alloc( void )
+{
+  struct pkgrc *pkgrc = NULL;
+
+  pkgrc = (struct pkgrc *)malloc( sizeof( struct pkgrc ) );
+  if( !pkgrc ) { FATAL_ERROR( "Cannot allocate memory" ); }
+  bzero( (void *)pkgrc, sizeof( struct pkgrc ) );
+
+  return pkgrc;
+}
+
+static void pkgrc_free( struct pkgrc *pkgrc )
+{
+  if( pkgrc )
+  {
+    if( pkgrc->name )    { free( pkgrc->name );    pkgrc->name    = NULL; }
+    if( pkgrc->version ) { free( pkgrc->version ); pkgrc->version = NULL; }
+    if( pkgrc->group )   { free( pkgrc->group );   pkgrc->group   = NULL; }
+
+    free( pkgrc );
+  }
+}
+
+static void __pkgrc_free_func( void *data, void *user_data )
+{
+  struct pkgrc *pkgrc = (struct pkgrc *)data;
+  if( pkgrc ) { pkgrc_free( pkgrc ); }
+}
+
+static void free_pkgrcl( void )
+{
+  if( pkgrcl ) { dlist_free( pkgrcl, __pkgrc_free_func ); pkgrcl = NULL; }
+}
+
+static void add_pkgrc( struct pkgrc *pkgrc )
+{
+  pkgrcl = dlist_append( pkgrcl, (void *)pkgrc );
+}
+
+static struct pkgrc *find_pkgrc( struct dlist *list, pid_t pid )
+{
+  if( !list ) return NULL;
+
+  while( list && list->data )
+  {
+    if( ((struct pkgrc *)list->data)->pid == pid ) { return (struct pkgrc *)list->data; }
+    list = dlist_next( list );
+  }
+
+  return NULL;
+}
+
+static void __remove_success_pkgrc( void *data, void *user_data )
+{
+  struct pkgrc *pkgrc = (struct pkgrc *)data;
+
+  if( pkgrc && pkgrc->status == 0 )
+  {
+    pkgrcl = dlist_remove( pkgrcl, (const void *)data );
+    pkgrc_free( pkgrc );
+  }
+}
+
+static void cleanup_pkgrcl( void )
+{
+  dlist_foreach( pkgrcl, __remove_success_pkgrc, NULL );
+}
+
+static void _print_pkgrcl( void *data, void *user_data )
+{
+  struct pkgrc *pkgrc = (struct pkgrc *)data;
+
+  if( pkgrc )
+  {
+    if( pkgrc->group )
+      fprintf( stderr, "%s/%s:%s:%d\n", pkgrc->group, pkgrc->name, pkgrc->version, pkgrc->status );
+    else
+      fprintf( stderr, "%s:%s:%d\n", pkgrc->name, pkgrc->version, pkgrc->status );
+  }
+}
+
+static void return_codes_list( void )
+{
+  if( pkgrcl )
+  {
+    dlist_foreach( pkgrcl, _print_pkgrcl, NULL );
+  }
+}
+/*
+  End of return status functions.
+ *********************************************/
+
+
+/*******************************
+  remove spaces at end of line:
+ */
+//static void skip_eol_spaces( char *s )
+//{
+//  char *p = (char *)0;
+//
+//  if( !s || *s == '\0' ) return;
+//
+//  p = s + strlen( s ) - 1;
+//  while( isspace( *p ) ) { *p-- = '\0'; }
+//}
+//
+//static char *skip_lead_spaces( char *s )
+//{
+//  char *p = (char *)0;
+//
+//  if( !s || *s == '\0' ) return p;
+//
+//  p = s; while( isspace( *p ) ) { ++p; }
+//
+//  return( p );
+//}
+
+static char *trim( char *s )
+{
+  char *p = (char *)0;
+
+  if( !s || *s == '\0' ) return p;
+
+  p = s + strlen( s ) - 1;
+  while( isspace( *p ) ) { *p-- = '\0'; }
+  p = s; while( isspace( *p ) ) { ++p; }
+
+  return( p );
+}
+
+
+static void read_pkglist_file( const char *fname )
+{
+  char *ln   = NULL;
+  char *line = NULL;
+  int   lnum = 0;
+
+  FILE *fp   = NULL;
+
+  if( !fname || (*fname == '\0') ) return;
+
+  fp = fopen( fname, "r" );
+  if( !fp )
+  {
+    FATAL_ERROR( "Cannot open '%s' PKGLIST file", basename( (char *)fname ) );
+  }
+
+  line = (char *)malloc( (size_t)PATH_MAX );
+  if( !line ) { FATAL_ERROR( "Cannot allocate memory" ); }
+  bzero( (void *)line, PATH_MAX );
+
+  while( (ln = fgets( line, PATH_MAX, fp )) )
+  {
+    char *p = NULL;
+    char *name = NULL, *vers = NULL, *desc = NULL, *ball = NULL, *proc = NULL, *prio = NULL;
+
+    ++lnum;
+
+    ln[strlen(ln) - 1] = '\0';  /* replace new-line symbol */
+    ln = trim( ln ); /* remove leading and trailing spaces */
+
+    if( *ln == '#' )
+    {
+      if( !strncmp( ln, "# required:", 11 ) )
+      {
+        char *n = NULL, *v = NULL, *g = NULL, *q = NULL;
+        char *rq = ln + 11;
+        rq = trim( rq );
+
+        n = rq;
+        if( (q = index( (const char *)n, '/' )) ) { *q = '\0'; g = n; n = ++q; g = trim( g ); }
+        if( (q = index( (const char *)n, '=' )) ) { *q = '\0'; v = ++q; n = trim( n ); }
+        v = trim( v );
+
+        if( n && v  )
+        {
+          struct pkg *pkg = pkg_alloc();
+
+          pkg->name    = xstrdup( (const char *)n );
+          pkg->version = xstrdup( (const char *)v );
+          if( g )
+            pkg->group = xstrdup( (const char *)g );
+
+          add_required( pkg );
+        }
+      }
+      continue; /* skip commented lines */
+    }
+
+    name = ln;
+    if( (p = index( (const char *)name, ':' )) ) { *p = '\0'; vers = ++p; name = trim( name ); } else continue;
+    if( (p = index( (const char *)vers, ':' )) ) { *p = '\0'; desc = ++p; vers = trim( vers ); } else continue;
+    if( (p = index( (const char *)desc, ':' )) ) { *p = '\0'; ball = ++p; desc = trim( desc ); } else continue;
+    if( (p = index( (const char *)ball, ':' )) ) { *p = '\0'; proc = ++p; ball = trim( ball ); } else continue;
+    if( (p = index( (const char *)proc, ':' )) ) { *p = '\0'; prio = ++p; proc = trim( proc ); } else continue;
+    prio = trim( prio );
+
+    if( name && vers && desc && ball && proc && prio )
+    {
+      char  *buf = NULL;
+      struct package *package = NULL;
+      char  *group = index( (const char *)ball, '/' );
+      enum _priority priority = OPTIONAL;
+
+      /*******************
+        Package priority:
+       */
+      if( strlen( (const char*)prio ) > 2 )
+      {
+        char *match = NULL;
+
+        to_lowercase( prio );
+        if( (match = strstr( prio, "req" )) && match == prio ) {
+          priority = REQUIRED;
+        }
+        else if( (match = strstr( prio, "rec" )) && match == prio ) {
+          priority = RECOMMENDED;
+        }
+        else if( (match = strstr( prio, "opt" )) && match == prio ) {
+          priority = OPTIONAL;
+        }
+        else if( (match = strstr( prio, "sk" )) && match == prio ) {
+          priority = SKIP;
+        }
+        else {
+          FATAL_ERROR( "%s: %d: Unknown '%s' priority value", basename( pkglist_fname ), lnum, prio );
+        }
+      }
+      else
+      {
+        FATAL_ERROR( "%s: %d: Unknown '%s' priority value", basename( pkglist_fname ), lnum, prio );
+      }
+
+      if( priority > install_priority ) continue;
+
+      buf = (char *)malloc( (size_t)PATH_MAX );
+      if( !buf ) { FATAL_ERROR( "Cannot allocate memory" ); }
+      bzero( (void *)buf, PATH_MAX );
+
+      package = package_alloc();
+
+      package->name        = xstrdup( (const char *)name );
+      package->version     = xstrdup( (const char *)vers );
+      package->description = xstrdup( (const char *)desc );
+
+      (void)sprintf( buf, "%s/%s", (const char *)srcdir, (const char *)ball );
+      {
+        enum _input_type type = IFMT_UNKNOWN;
+        char             uncompress = '\0';
+
+        type = check_package_file( &uncompress, (const char *)&buf[0] );
+        if( type != IFMT_PKG )
+        {
+          FATAL_ERROR( "Unknown format of '%s' package file", (const char *)&buf[0] );
+        }
+      }
+      package->tarball     = xstrdup( (const char *)&buf[0] );
+
+      free( buf );
+
+      package->priority = priority;
+
+      /********************
+        Install procedure:
+       */
+      if( strlen( (const char*)proc ) > 5 )
+      {
+        char *match = NULL;
+
+        to_lowercase( proc );
+        if( (match = strstr( proc, "install" )) && match == proc ) {
+          package->procedure = INSTALL;
+        }
+        else if( (match = strstr( proc, "update" )) && match == proc ) {
+          package->procedure = UPDATE;
+        }
+        else {
+          FATAL_ERROR( "%s: %d: Unknown '%s' procedure value", basename( pkglist_fname ), lnum, proc );
+        }
+      }
+      else
+      {
+        FATAL_ERROR( "%s: %d: Unknown '%s' procedure value", basename( pkglist_fname ), lnum, proc );
+      }
+
+      if( group )
+      {
+        *group = '\0';
+        group = ball;
+        package->group = xstrdup( (const char *)group );
+      }
+
+      add_package( package );
+    }
+
+  } /* End of while( ln = fgets( line ) ) */
+
+  free( line );
+  fclose( fp );
+}
+
+
+static void check_requires( void )
+{
+  if( requires )
+  {
+    exit_status = 1;
+
+    fprintf( stdout, "\nThe input '%s' has the list of %d required packages.\n\n", basename( pkglist_fname ), dlist_length( requires ) );
+
+    if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); }
+    free_resources();
+    exit( exit_status );
+  }
+}
+
+#if defined( HAVE_DIALOG )
+static DIALOG_LISTITEM *alloc_select_items( void )
+{
+  DIALOG_LISTITEM *items   = NULL;
+  int i = 0, num = dlist_length( packages );
+  struct dlist *list = packages, *next = NULL;
+
+  items = (DIALOG_LISTITEM *)malloc( (num + 1) * sizeof(DIALOG_LISTITEM) );
+  if( !items )  { FATAL_ERROR( "Cannot allocate memory" ); }
+  bzero( (void *)items, (num + 1) * sizeof(DIALOG_LISTITEM) );
+
+  while( list )
+  {
+    struct package *package = NULL;
+
+    next = dlist_next( list );
+    package = (struct package *)list->data;
+    if( package )
+    {
+#define COLUMN_LENGHT  23         /* 22 symbols for name + " [UP] " */
+#define NAME_LENGHT    (COLUMN_LENGHT - 7) /* strlen(" [UP] ") + 1; */
+#define UPDATE_SUFFIX  " [UP] "
+#define INSTALL_SUFFIX " [in] "
+
+      char *name = (char *)malloc( (size_t)COLUMN_LENGHT );
+      if( !name ) { FATAL_ERROR( "Cannot allocate memory" ); }
+      bzero( (void *)name, (size_t)COLUMN_LENGHT );
+
+      if( strlen( package->name ) > (size_t)NAME_LENGHT )
+      {
+        (void)strncpy( name, (const char *)package->name, NAME_LENGHT - 2 );
+        (void)strcat( name, ".." );
+      }
+      else
+      {
+        (void)strcpy( name, (const char *)package->name );
+      }
+
+      if( package->procedure == UPDATE )
+      {
+        int p = strlen( name );
+        while( p < NAME_LENGHT ) { name[p] = ' '; ++p; }
+        name[p] = '\0';
+        (void)strcat( name, UPDATE_SUFFIX );
+      }
+      else
+      {
+        int p = strlen( name );
+        /* while( p < (COLUMN_LENGHT - 1) ) { name[p] = ' '; ++p; } */
+        while( p < NAME_LENGHT ) { name[p] = ' '; ++p; }
+        name[p] = '\0';
+        (void)strcat( name, INSTALL_SUFFIX );
+      }
+
+      items[i].name = name;
+      items[i].text = xstrdup( (const char *)package->description );
+      if( package->group )
+        items[i].help = xstrdup( (const char *)package->group );
+      items[i].state = 1;
+    }
+    ++i;
+    list = next;
+  }
+  return items;
+}
+
+static void free_select_items( DIALOG_LISTITEM *items )
+{
+  DIALOG_LISTITEM *p = items;
+
+  if( !p ) return;
+
+  while( p->name ) { free( p->name ); free( p->text ); if( p->help ) free( p->help ); ++p; }
+
+  free( items );
+}
+
+static void remove_unselected_packages( DIALOG_LISTITEM *items )
+{
+  DIALOG_LISTITEM *p   = items;
+  struct dlist    *rem = NULL, *list = NULL, *next = NULL;
+  int              n   = 0;
+
+  if( !p ) return;
+
+  while( p->name )
+  {
+    /* copy packages list item to the local list: */
+    if( !p->state ) { list = dlist_append( list, dlist_nth_data( packages, n ) ); }
+    ++p; ++n;
+  }
+
+  /****************************************************
+    remove items of the local list from packages list:
+   */
+  rem = list;
+  while( rem )
+  {
+    next = dlist_next( rem );
+    packages = dlist_remove( packages, rem->data );
+    rem = next;
+  }
+
+  /***************************
+    free unselected packages:
+   */
+  dlist_free( list, __package_free_func );
+}
+#endif
+
+
+/*********************************************
+  Progress functions:
+ */
+static void show_install_con_progress( const char *title, int percent )
+{
+#define GAUGE_LENGTH  68
+  size_t  prefix = strlen( title ) + 2; /* title + ' [' */
+  size_t  suffix = 6;                   /* '] 100%' */
+  size_t  length = prefix  + GAUGE_LENGTH + suffix;
+  int     i, ctx = GAUGE_LENGTH * percent / 100;
+
+  if( percent <   0 ) percent = 0;
+  if( percent > 100 ) percent = 100;
+
+  printf( "\033[1A" );               /* move the cursor up 1 line   */
+  printf( "\033[%dD", (int)length ); /* move cursor to start of line */
+  printf( "%s [", title );
+
+  for( i = 0; i < ctx; ++i )     { fprintf( stdout, "\u2588" ); }
+  for( ; i < GAUGE_LENGTH; ++i ) { fprintf( stdout, " " );      }
+
+  printf( "] %3d%%\n", percent );
+  fflush( stdout );
+}
+
+static void show_progress( void )
+{
+  const char *title   = "Install:";
+  const char *message = "\n"
+                        "Please wait for install all specified packages:\n"
+                        "\n\n";
+
+  if( install_mode != CONSOLE )
+  {
+#if defined( HAVE_DIALOG )
+    show_install_dlg_progress( 0 );
+#else
+    fprintf( stdout, "%s", message );
+    show_install_con_progress( title, 0 );
+#endif
+  }
+  else
+  {
+    fprintf( stdout, "%s", message );
+    show_install_con_progress( title, 0 );
+  }
+
+}
+
+static void update_progress( int percent )
+{
+  const char *title   = "Install:";
+
+  if( install_mode != CONSOLE )
+  {
+#if defined( HAVE_DIALOG )
+    show_install_dlg_progress( percent );
+#else
+    show_install_con_progress( title, percent );
+#endif
+  }
+  else
+  {
+    show_install_con_progress( title, percent );
+  }
+}
+
+static void stop_progress( void )
+{
+  const char *title   = "Install:";
+
+  if( install_mode != CONSOLE )
+  {
+#if defined( HAVE_DIALOG )
+    show_install_dlg_progress( 100 );
+#else
+    show_install_con_progress( title, 100 );
+    fprintf( stdout, "\n" );
+#endif
+  }
+  else
+  {
+    show_install_con_progress( title, 100 );
+    fprintf( stdout, "\n" );
+  }
+}
+/*
+  End of progress functions.
+ *********************************************/
+
+
+
+/*********************************************
+  Install functions.
+ */
+static void install_package( struct package *package )
+{
+  int   len = 0;
+  char *cmd = NULL, *opt = NULL, *out = "> /dev/null 2>&1";
+
+  if( !package ) return;
+
+  opt = (char *)malloc( (size_t)PATH_MAX );
+  if( !opt ) { FATAL_ERROR( "Cannot allocate memory" ); }
+  bzero( (void *)opt, PATH_MAX );
+  opt[0] = ' ';
+
+  if( gpgck ) (void)sprintf( opt, "--gpg-verify " );
+  if( (install_mode != CONSOLE) && !parallel && !progress ) (void)strcat( opt, "--info-dialog " );
+  if( parallel ) (void)strcat( opt, "--ignore-chrefs-errors " );
+  if( (install_mode == CONSOLE) && !parallel && !progress ) out = " ";
+
+  cmd = (char *)malloc( (size_t)PATH_MAX );
+  if( !cmd ) { FATAL_ERROR( "Cannot allocate memory" ); }
+  bzero( (void *)cmd, PATH_MAX );
+
+  len = snprintf( &cmd[0], PATH_MAX,
+                  "%s/%s-package %s --priority=%s --root=%s %s %s",
+                  selfdir, strproc( package->procedure ), opt,
+                  strprio( package->priority, 1 ),
+                  root, package->tarball, out );
+  if( len == 0 || len == PATH_MAX - 1 )
+  {
+    FATAL_ERROR( "Cannot install %s-%s package", package->name, package->version );
+  }
+  if( parallel )
+  {
+    struct pkgrc *pkgrc = pkgrc_alloc();
+
+    pkgrc->name    = xstrdup( (const char *)package->name );
+    pkgrc->version = xstrdup( (const char *)package->version );
+    if( package->group )
+      pkgrc->group = xstrdup( (const char *)package->group );
+    pkgrc->pid     = sys_exec_command( cmd );
+
+    add_pkgrc( pkgrc );
+    ++__child;
+  }
+  else
+  {
+    pid_t p = (pid_t) -1;
+    int  rc = 0;
+
+    p = sys_exec_command( cmd );
+    rc = sys_wait_command( p, (char *)NULL, PATH_MAX );
+    if( rc != 0 && rc != 31 )
+    {
+      FATAL_ERROR( "Cannot install '%s-%s' package", package->name, package->version );
+    }
+    ++__successful;
+  }
+
+  free( cmd );
+  free( opt );
+}
+
+static void _serial_install_package( void *data, void *user_data )
+{
+  struct package *package = (struct package *)data;
+  int    percent;
+
+  if( package ) { install_package( package ); }
+
+  if( progress )
+  {
+    percent = ( __successful < __all ) ? __successful * 100 / __all : 100;
+    update_progress( percent );
+  }
+}
+
+static void serial_install_packages( void )
+{
+  if( progress )
+  {
+    show_progress();
+  }
+
+  dlist_foreach( packages, _serial_install_package, NULL );
+
+  if( progress )
+  {
+    stop_progress();
+  }
+}
+
+
+static void *install_process( void *args )
+{
+  struct dlist *list = packages, *next = NULL;
+
+  int nstreams = ncpus * 2; /* two concurents for CPU */
+
+  while( list )
+  {
+    struct package *package = NULL;
+
+    next = dlist_next( list );
+    package = (struct package *)list->data;
+    if( package )
+    {
+      install_package( package );
+    }
+    list = next;
+
+    /* wait for available CPU: */
+    while( (__child - __terminated) > nstreams ) usleep( WAIT_USEC_FOR_CHILD );
+  }
+
+  return NULL;
+}
+
+static void parallel_install_packages( void )
+{
+  pthread_t install_process_id;
+  int       status;
+
+  /* Start the parallel installation process: */
+  status = pthread_create( &install_process_id, NULL, install_process, NULL );
+  if( status != 0 )
+  {
+    FATAL_ERROR( "Cannot start parallel installation process" );
+  }
+  (void)pthread_detach( install_process_id );
+}
+
+/*
+  End of install functions.
+ *********************************************/
+
+
+
+
+static void dialogrc( void )
+{
+  struct stat st;
+  char  *tmp = NULL;
+
+  tmp = (char *)malloc( (size_t)PATH_MAX );
+  if( !tmp ) { FATAL_ERROR( "Cannot allocate memory" ); }
+  bzero( (void *)tmp, PATH_MAX );
+
+  /* imagine that the utility is in /sbin directory: */
+  (void)sprintf( &tmp[0], "%s/../usr/share/%s/.dialogrc", selfdir, PACKAGE_NAME );
+  if( stat( (const char *)&tmp[0], &st ) == -1 )
+  {
+    /* finaly assume that /usr/sbin is a sbindir: */
+    (void)sprintf( &tmp[0], "%s/../../usr/share/%s/.dialogrc", selfdir, PACKAGE_NAME );
+  }
+
+  setenv( "DIALOGRC", (const char *)&tmp[0], 1 );
+
+  free( tmp );
+}
+
+static char *get_curdir( void )
+{
+  char *cwd = NULL;
+
+  cwd = (char *)malloc( PATH_MAX );
+  if( !cwd ) { FATAL_ERROR( "Cannot allocate memory" ); }
+  bzero( (void *)cwd, PATH_MAX );
+
+  if( getcwd( cwd, (size_t)PATH_MAX ) != NULL )
+  {
+    char *p = NULL;
+    remove_trailing_slash( cwd );
+    p = xstrdup( (const char *)cwd );
+    free( cwd );
+    return p;
+  }
+  else
+  {
+    FATAL_ERROR( "Cannot get absolute path to current directory" );
+  }
+
+  return (char *)NULL;
+}
+
+
+/*********************************************
+  Get directory where this program is placed:
+ */
+char *get_selfdir( void )
+{
+  char    *buf = NULL;
+  ssize_t  len;
+
+  buf = (char *)malloc( PATH_MAX );
+  if( !buf )
+  {
+    FATAL_ERROR( "Cannot allocate memory" );
+  }
+
+  bzero( (void *)buf, PATH_MAX );
+  len = readlink( "/proc/self/exe", buf, (size_t)PATH_MAX );
+  if( len > 0 && len < PATH_MAX )
+  {
+    char *p = xstrdup( (const char *)dirname( buf ) );
+    free( buf );
+    return p;
+  }
+  FATAL_ERROR( "Cannot determine self directory. Please mount /proc filesystem" );
+}
+
+void set_stack_size( void )
+{
+  const rlim_t   stack_size = 16 * 1024 * 1024; /* min stack size = 16 MB */
+  struct rlimit  rl;
+  int ret;
+
+  ret = getrlimit( RLIMIT_STACK, &rl );
+  if( ret == 0 )
+  {
+    if( rl.rlim_cur < stack_size )
+    {
+      rl.rlim_cur = stack_size;
+      ret = setrlimit( RLIMIT_STACK, &rl );
+      if( ret != 0 )
+      {
+        fprintf(stderr, "setrlimit returned result = %d\n", ret);
+        FATAL_ERROR( "Cannot set stack size" );
+      }
+    }
+  }
+}
+
+
+int main( int argc, char *argv[] )
+{
+  gid_t  gid;
+
+  set_signal_handlers();
+
+  gid = getgid();
+  setgroups( 1, &gid );
+
+  ncpus = get_nprocs();
+
+  fatal_error_hook = fatal_error_actions;
+
+  selfdir = get_selfdir();
+  curdir  = get_curdir();
+  dialogrc();
+
+  errlog = stderr;
+
+  program = basename( argv[0] );
+  get_args( argc, argv );
+
+  /* set_stack_size(); */
+
+  tmpdir = _mk_tmpdir();
+  if( !tmpdir )
+  {
+    FATAL_ERROR( "Cannot create temporary directory" );
+  }
+
+  /***********************************************************
+    Fill requires and packages lists, check package tarballs,
+    skip unnesessary packages according to --priority option:
+   */
+  read_pkglist_file( (const char *)pkglist_fname );
+
+  /* check only the list of requires in the input PKGLIST: */
+  if( rqck ) check_requires();
+
+#if defined( HAVE_DIALOG )
+  if( install_mode == MENUDIALOG )
+  {
+    int  status = 0, num   = dlist_length( packages );
+    DIALOG_LISTITEM *items = alloc_select_items();
+
+    status = select_packages_box( items, num, 0, 0 );
+    if( !status )
+    {
+      remove_unselected_packages( items );
+      free_select_items( items );
+    }
+    else
+    {
+      /* Abort installation: */
+      if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); }
+      free_resources();
+
+      exit( exit_status );
+    }
+  }
+#endif
+
+
+  if( parallel )
+  {
+    /************************
+      parallel installation:
+     */
+    int percent = 0;
+
+    __all  = dlist_length( packages );
+    __done = 0; __child = 0;
+
+    __terminated = 0; __successful = 0;
+
+    show_progress();
+
+    parallel_install_packages();
+
+    if( __terminated < __all )
+    {
+      while( !__done )
+      {
+        percent = ( __terminated < __all ) ? __terminated * 100 / __all : 100;
+
+        update_progress( percent );
+        usleep( WAIT_USEC_FOR_CHILD );
+      }
+    }
+
+    __done = 0; __child = 0;
+
+    stop_progress();
+
+    if( __successful < __terminated ) { percent = __successful * 100 / __terminated; }
+    else                              { percent = 100; }
+
+    __terminated = 0; __successful = 0;
+
+    if( install_mode != CONSOLE )
+    {
+#if defined( HAVE_DIALOG )
+      char *msg = NULL;
+
+      msg = (char *)malloc( (size_t)80 );
+      if( !msg ) { FATAL_ERROR( "Cannot allocate memory" ); }
+      bzero( (void *)msg, 80 );
+
+      (void)sprintf( msg, "\nSuccessfully installed %d%% of %d specified packages.\n", percent, __all );
+
+      (void)info_box( " \\Z0INSTALL PACKAGES\\Zn ", msg, 5, 0, 0 );
+
+      free( msg );
+#else
+      fprintf( stdout, "\nSuccessfully installed %d%% of %d specified packages.\n\n", percent, __all );
+#endif
+    }
+    else
+    {
+      fprintf( stdout, "\nSuccessfully installed %d%% of %d specified packages.\n\n", percent, __all );
+    }
+
+    cleanup_pkgrcl();  /* remove successfully installed packages from return codes list */
+    if( pkgrcl && error_pkgs_list )
+    {
+      return_codes_list();
+    }
+  }
+  else
+  {
+    /**********************
+      serial installation:
+     */
+
+    __successful = 0;
+    __all        = dlist_length( packages );
+
+    serial_install_packages();
+
+    if( install_mode != CONSOLE )
+    {
+#if defined( HAVE_DIALOG )
+      info_box( " \\Z4INSTALL PACKAGES\\Zn ",
+                "\nAll of specified packages have been installed.\n", 5, 0, 0 );
+#else
+      fprintf( stdout, "\nAll of specified packages have been installed.\n\n" );
+#endif
+    }
+    else
+    {
+      fprintf( stdout, "\nAll of specified packages have been installed.\n\n" );
+    }
+
+  }
+
+
+  if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); }
+  free_resources();
+
+  exit( exit_status );
+}