Radix cross Linux package tools

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

3 Commits   0 Branches   2 Tags
author: kx <kx@radix.pro> 2023-04-11 01:18:34 +0300 committer: kx <kx@radix.pro> 2023-04-11 01:18:34 +0300 commit: 11c606a6888dc269ef018359469a7276c3ad8f67 parent: 8c55752ed5b29a22fdab9faaa6ff27b7cafa6791
Commit Summary:
Version 0.2.1
Diffstat:
1 file changed, 1064 insertions, 0 deletions
diff --git a/src/pkglog.c b/src/pkglog.c
new file mode 100644
index 0000000..12ab381
--- /dev/null
+++ b/src/pkglog.c
@@ -0,0 +1,1316 @@
+
+/**********************************************************************
+
+  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/types.h>
+#include <stdint.h>
+#include <dirent.h>
+#include <sys/stat.h> /* chmod(2)    */
+#include <fcntl.h>
+#include <linux/limits.h>
+#include <alloca.h>   /* alloca(3)   */
+#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/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>
+
+#define PROGRAM_NAME "pkglog"
+
+#include <defs.h>
+
+
+char *program     = PROGRAM_NAME;
+char *destination = NULL, *srcdir = NULL, *pkginfo_fname = NULL, *output_fname = NULL;
+int   exit_status = EXIT_SUCCESS; /* errors counter */
+char *selfdir     = NULL;
+
+int   mkgroupdir  = 0;
+
+int   rm_srcdir_at_exit = 0;
+
+char           *pkgname = NULL,
+                *pkgver = NULL,
+                  *arch = NULL,
+            *distroname = NULL,
+             *distrover = NULL,
+                 *group = NULL,
+                   *url = NULL,
+               *license = NULL,
+     *uncompressed_size = NULL,
+           *total_files = NULL;
+
+FILE *pkginfo = NULL;
+FILE *output  = NULL;
+
+
+#define FREE_PKGLOG_VARIABLES() \
+  if( pkgname )           { free( pkgname );           } pkgname = NULL;            \
+  if( pkgver )            { free( pkgver );            } pkgver = NULL;             \
+  if( arch )              { free( arch );              } arch = NULL;               \
+  if( distroname )        { free( distroname );        } distroname = NULL;         \
+  if( distrover )         { free( distrover );         } distrover = NULL;          \
+  if( group )             { free( group );             } group = NULL;              \
+  if( url )               { free( url );               } url = NULL;                \
+  if( license )           { free( license );           } license = NULL;            \
+  if( uncompressed_size ) { free( uncompressed_size ); } uncompressed_size = NULL;  \
+  if( total_files )       { free( total_files );       } total_files = NULL
+
+void free_resources()
+{
+  if( selfdir )       { free( selfdir );       selfdir       = NULL; }
+  if( srcdir )        { free( srcdir );        srcdir        = NULL; }
+  if( destination )   { free( destination );   destination   = NULL; }
+  if( pkginfo_fname ) { free( pkginfo_fname ); pkginfo_fname = NULL; }
+  if( output_fname )
+  {
+    if( output ) { (void)fflush( output ); fclose( output ); output = NULL; }
+    free( output_fname ); output_fname = NULL;
+  }
+
+  FREE_PKGLOG_VARIABLES();
+}
+
+void usage()
+{
+  free_resources();
+
+  fprintf( stdout, "\n" );
+  fprintf( stdout, "Usage: %s [options] <dir|pkginfo|package>\n", program );
+  fprintf( stdout, "\n" );
+  fprintf( stdout, "Read information from package's service files and create PKGLOG file\n" );
+  fprintf( stdout, "in the destination directory. <pkginfo> is a full name of '.PKGINFO'\n" );
+  fprintf( stdout, "service file of package.  Rest of package's  serfice files should be\n" );
+  fprintf( stdout, "present in  the  directory of '.PKGINFO'.  If last argument is <dir>\n" );
+  fprintf( stdout, "then pkglog utility will try to read '.PKGINFO' and rest of service\n" );
+  fprintf( stdout, "files from directory given by <dir> argument.\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,--destination=<DIR>     Target directory to save output PKGLOG.\n" );
+  fprintf( stdout, "  -m,--mkgroupdir            Create group subdirectory in the PKGLOG\n" );
+  fprintf( stdout, "                             target directory.\n" );
+  fprintf( stdout, "\n" );
+  fprintf( stdout, "Parameter:\n" );
+  fprintf( stdout, "  <dir|pkginfo|package>      Directory wich contains the package's\n"  );
+  fprintf( stdout, "                             service files or path to .PKGINFO file\n"  );
+  fprintf( stdout, "                             or package tarball.\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( rm_srcdir_at_exit ) _rm_tmpdir( (const char *)srcdir );
+  if( output_fname )
+  {
+    if( output ) { (void)fflush( output ); fclose( output ); output = NULL; }
+    (void)unlink( output_fname );
+    free( output_fname ); output_fname = NULL;
+  }
+  free_resources();
+}
+
+void sigint( int signum )
+{
+  (void)signum;
+
+  if( rm_srcdir_at_exit ) _rm_tmpdir( (const char *)srcdir );
+  if( output_fname )
+  {
+    if( output ) { (void)fflush( output ); fclose( output ); output = NULL; }
+    (void)unlink( output_fname );
+    free( output_fname ); output_fname = NULL;
+  }
+  free_resources();
+}
+
+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 );
+
+  memset( &sa, 0, sizeof( sa ) );  /* ignore SIGPIPE */
+  sa.sa_handler = SIG_IGN;
+  sa.sa_flags = 0;
+  sigaction( SIGPIPE, &sa, NULL );
+
+  /* System V fork+wait does not work if SIGCHLD is ignored */
+  signal( SIGCHLD, SIG_DFL );
+}
+
+enum _pkginfo_type
+{
+  PKGINFO_TEXT = 0,
+  PKGINFO_GZ,
+  PKGINFO_BZ2,
+  PKGINFO_XZ,
+  PKGINFO_TAR,
+
+  PKGINFO_UNKNOWN
+};
+
+static enum _pkginfo_type pkginfo_type = PKGINFO_UNKNOWN;
+static char uncompress[2] = { 0, 0 };
+
+
+static enum _pkginfo_type check_pkginfo_file( const char *fname )
+{
+  struct stat st;
+  size_t pkginfo_size = 0;
+  unsigned char buf[8];
+  int rc, fd;
+
+  /* SIGNATURES: https://www.garykessler.net/library/file_sigs.html */
+
+  uncompress[0] = '\0';
+
+  if( stat( fname, &st ) == -1 )
+  {
+    FATAL_ERROR( "Cannot access %s file: %s", basename( (char *)fname ), strerror( errno ) );
+  }
+
+  pkginfo_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 )
+  {
+    FATAL_ERROR( "Unknown type of input file %s", basename( (char *)fname ) );
+  }
+  buf[7] = '\0';
+
+  /* TEXT */
+  if( !strncmp( (const char *)&buf[0], "pkgname", 7 ) )
+  {
+    close( fd ); return PKGINFO_TEXT;
+  }
+
+  /* GZ */
+  if( buf[0] == 0x1F && buf[1] == 0x8B && buf[2] == 0x08 )
+  {
+    uncompress[0] = 'x';
+    close( fd ); return PKGINFO_GZ;
+  }
+
+  /* BZ2 */
+  if( buf[0] == 0x42 && buf[1] == 0x5A && buf[2] == 0x68 )
+  {
+    uncompress[0] = 'j';
+    close( fd ); return PKGINFO_BZ2;
+  }
+
+  /* XZ */
+  if( buf[0] == 0xFD && buf[1] == 0x37 && buf[2] == 0x7A &&
+      buf[3] == 0x58 && buf[4] == 0x5A && buf[5] == 0x00   )
+  {
+    uncompress[0] = 'J';
+    close( fd ); return PKGINFO_XZ;
+  }
+
+  if( pkginfo_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 PKGINFO_TAR;
+    }
+  }
+
+  close( fd ); return PKGINFO_UNKNOWN;
+}
+
+
+void get_args( int argc, char *argv[] )
+{
+  const char* short_options = "hvmd:";
+
+  const struct option long_options[] =
+  {
+    { "help",        no_argument,       NULL, 'h' },
+    { "version",     no_argument,       NULL, 'v' },
+    { "destination", required_argument, NULL, 'd' },
+    { "mkgroupdir",  no_argument,       NULL, 'm' },
+    { 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':
+      {
+        if( optarg != NULL )
+        {
+          destination = xstrdup( (const char *)optarg );
+          remove_trailing_slash( destination );
+        }
+        else
+          /* option is present but without value */
+          usage();
+        break;
+      }
+      case 'm':
+      {
+        mkgroupdir = 1;
+        break;
+      }
+
+      case '?': default:
+      {
+        usage();
+        break;
+      }
+    }
+  }
+
+  if( destination == NULL )
+  {
+    char cwd[PATH_MAX];
+    if( getcwd( cwd, sizeof(cwd) ) != NULL )
+      destination = xstrdup( (const char *)cwd );
+    else
+      destination = xstrdup( "." );
+  }
+
+  /* last command line argument is the PKGLOG file */
+  if( optind < argc )
+  {
+    struct stat st;
+    char  *buf = NULL;
+
+    buf = (char *)malloc( strlen( (const char *)argv[optind] ) + 10 );
+    if( !buf )
+    {
+      FATAL_ERROR( "Cannot allocate memory" );
+    }
+
+    (void)strcpy( buf, (const char *)argv[optind++] );
+    remove_trailing_slash( (char *)&buf[0] );
+
+    if( stat( (const char *)&buf[0], &st ) == -1 )
+    {
+      FATAL_ERROR( "Cannot access '%s' file: %s", basename( buf ), strerror( errno ) );
+    }
+
+    if( S_ISDIR(st.st_mode) )
+    {
+      /* Add .PKGINFO to the input dir name: */
+      (void)strcat( buf, "/.PKGINFO" );
+    }
+
+    pkginfo_fname = xstrdup( (const char *)&buf[0] );
+    if( pkginfo_fname == NULL )
+    {
+      usage();
+    }
+
+    free( buf );
+
+    pkginfo_type = check_pkginfo_file( (const char *)pkginfo_fname );
+    if( pkginfo_type == PKGINFO_UNKNOWN )
+    {
+      ERROR( "%s: Unknown input file format", basename( pkginfo_fname ) );
+      usage();
+    }
+  }
+  else
+  {
+    usage();
+  }
+}
+
+
+/*
+  Especialy for pkginfo lines.
+  Remove leading spaces and take non-space characters only:
+ */
+static char *skip_spaces( char *s )
+{
+  char *q, *p = (char *)0;
+
+  if( !s || *s == '\0' ) return p;
+
+  p = s;
+
+  while( (*p == ' ' || *p == '\t') && *p != '\0' ) { ++p; } q = p;
+  while(  *q != ' ' && *q != '\t'  && *q != '\0' ) { ++q; } *q = '\0';
+
+  if( *p == '\0' ) return (char *)0;
+
+  return( xstrdup( (const char *)p ) );
+}
+
+/*
+  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'; }
+}
+
+
+void write_pkginfo( void )
+{
+  char *ln   = NULL;
+  char *line = NULL;
+
+  if( pkginfo_fname != NULL )
+  {
+    pkginfo = fopen( (const char *)pkginfo_fname, "r" );
+    if( !pkginfo )
+    {
+      FATAL_ERROR( "Cannot open %s file", pkginfo_fname );
+    }
+  }
+
+  line = (char *)malloc( (size_t)PATH_MAX );
+  if( !line )
+  {
+    FATAL_ERROR( "Cannot allocate memory" );
+  }
+
+  while( (ln = fgets( line, PATH_MAX, pkginfo )) )
+  {
+    char *match = NULL;
+
+    ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol      */
+    skip_eol_spaces( ln );     /* remove spaces at end-of-line */
+
+    if( (match = strstr( ln, "pkgname" )) && match == ln ) {
+      char *p = index( match, '=' ) + 1;
+      if( p != NULL ) pkgname = skip_spaces( p );
+    }
+    if( (match = strstr( ln, "pkgver" )) && match == ln ) {
+      char *p = index( match, '=' ) + 1;
+      if( p != NULL ) pkgver = skip_spaces( p );
+    }
+    if( (match = strstr( ln, "arch" )) && match == ln ) {
+      char *p = index( match, '=' ) + 1;
+      if( p != NULL ) arch = skip_spaces( p );
+    }
+    if( (match = strstr( ln, "distroname" )) && match == ln ) {
+      char *p = index( match, '=' ) + 1;
+      if( p != NULL ) distroname = skip_spaces( p );
+    }
+    if( (match = strstr( ln, "distrover" )) && match == ln ) {
+      char *p = index( match, '=' ) + 1;
+      if( p != NULL ) distrover = skip_spaces( p );
+    }
+
+
+    if( (match = strstr( ln, "group" )) && match == ln ) {
+      char *p = index( match, '=' ) + 1;
+      if( p != NULL ) group = skip_spaces( p );
+    }
+    /* variable short_description="..." is not stored in the PKGLOG file */
+    if( (match = strstr( ln, "url" )) && match == ln ) {
+      char *p = index( match, '=' ) + 1;
+      if( p != NULL ) url = skip_spaces( p );
+    }
+    if( (match = strstr( ln, "license" )) && match == ln ) {
+      char *p = index( match, '=' ) + 1;
+      if( p != NULL ) license = skip_spaces( p );
+    }
+    if( (match = strstr( ln, "uncompressed_size" )) && match == ln ) {
+      char *p = index( match, '=' ) + 1;
+      if( p != NULL ) uncompressed_size = skip_spaces( p );
+    }
+    if( (match = strstr( ln, "total_files" )) && match == ln ) {
+      char *p = index( match, '=' ) + 1;
+      if( p != NULL ) total_files = skip_spaces( p );
+    }
+  }
+
+  free( line );
+
+  if( pkgname && pkgver && arch && distroname && distrover )
+  {
+    int   len;
+    char *buf = NULL;
+
+    buf = (char *)malloc( (size_t)PATH_MAX );
+    if( !buf )
+    {
+      FATAL_ERROR( "Cannot allocate memory" );
+    }
+
+    if( mkgroupdir && group )
+    {
+      len = snprintf( buf, PATH_MAX, "%s/%s",
+                                      destination, group );
+      if( len == 0 || len == PATH_MAX - 1 )
+      {
+        FATAL_ERROR( "Cannot create output file" );
+      }
+
+      if( _mkdir_p( buf, S_IRWXU | S_IRWXG | S_IRWXO ) != 0 )
+      {
+        FATAL_ERROR( "Cannot create output directory" );
+      }
+
+      len = snprintf( buf, PATH_MAX, "%s/%s/%s-%s-%s-%s-%s",
+                                      destination, group, pkgname, pkgver, arch, distroname, distrover );
+      if( len == 0 || len == PATH_MAX - 1 )
+      {
+        FATAL_ERROR( "Cannot create output file" );
+      }
+    }
+    else
+    {
+      len = snprintf( buf, PATH_MAX, "%s/%s-%s-%s-%s-%s",
+                                      destination, pkgname, pkgver, arch, distroname, distrover );
+      if( len == 0 || len == PATH_MAX - 1 )
+      {
+        FATAL_ERROR( "Cannot create output file" );
+      }
+    }
+    output_fname = xstrdup( (const char *)&buf[0] );
+    if( output_fname )
+    {
+      output = fopen( (const char *)output_fname, "w" );
+      if( !output )
+      {
+        FATAL_ERROR( "Cannot create %s file", output_fname );
+      }
+    }
+
+    free( buf );
+
+    fprintf( output, "PACKAGE NAME: %s\n",    pkgname    );
+    fprintf( output, "PACKAGE VERSION: %s\n", pkgver     );
+    fprintf( output, "ARCH: %s\n",            arch       );
+    fprintf( output, "DISTRO: %s\n",          distroname );
+    fprintf( output, "DISTRO VERSION: %s\n",  distrover  );
+  }
+  else
+  {
+    FATAL_ERROR( "Invalid input .PKGINFO file" );
+  }
+
+  if( group )
+    fprintf( output, "GROUP: %s\n", group );
+  if( url )
+    fprintf( output, "URL: %s\n", url );
+  if( license )
+    fprintf( output, "LICENSE: %s\n", license );
+  if( uncompressed_size )
+    fprintf( output, "UNCOMPRESSED SIZE: %s\n", uncompressed_size );
+  if( total_files )
+    fprintf( output, "TOTAL FILES: %s\n", total_files );
+
+  /* reference counter of not installed package is always zero */
+  fprintf( output, "REFERENCE COUNTER: 0\n" );
+}
+
+
+void write_requires( void )
+{
+  struct stat sb;
+  char  *buf = NULL;
+
+  if( output == NULL && output_fname == NULL )
+  {
+    FATAL_ERROR( "Unable to access output file" );
+  }
+
+  buf = (char *)malloc( (size_t)PATH_MAX );
+  if( !buf )
+  {
+    FATAL_ERROR( "Cannot allocate memory" );
+  }
+
+  fprintf( output, "REQUIRES:\n" );
+
+  bzero( (void *)buf, PATH_MAX );
+  (void)sprintf( (char *)&buf[0], "%s/.REQUIRES", srcdir );
+
+  /* check if path exists and is a regular file */
+  if( stat( (const char *)&buf[0], &sb ) == 0 && S_ISREG(sb.st_mode) )
+  {
+    char *ln   = NULL;
+    char *line = NULL;
+
+    FILE *input;
+
+    input = fopen( (const char *)&buf[0], "r" );
+    if( !input )
+    {
+      FATAL_ERROR( "Unable to access %s file", (char *)&buf[0] );
+    }
+
+    line = (char *)malloc( (size_t)PATH_MAX );
+    if( !line )
+    {
+      FATAL_ERROR( "Cannot allocate memory" );
+    }
+
+    /* cat .REQUIRES >> PKGLOG */
+    while( (ln = fgets( line, PATH_MAX, input )) )
+    {
+      ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol      */
+      skip_eol_spaces( ln );     /* remove spaces at end-of-line */
+
+      /* print non-empty lines */
+      if( *ln )
+      {
+        fprintf( output, "%s\n", ln );
+      }
+    }
+    free( line );
+  }
+  free( buf );
+}
+
+
+void write_description( void )
+{
+  struct stat sb;
+  char  *buf = NULL;
+
+  if( output == NULL && output_fname == NULL )
+  {
+    FATAL_ERROR( "Unable to access output file" );
+  }
+
+  buf = (char *)malloc( (size_t)PATH_MAX );
+  if( !buf )
+  {
+    FATAL_ERROR( "Cannot allocate memory" );
+  }
+
+  fprintf( output, "PACKAGE DESCRIPTION:\n" );
+
+  bzero( (void *)buf, PATH_MAX );
+  (void)sprintf( (char *)&buf[0], "%s/.DESCRIPTION", srcdir );
+
+  /* check if path exists and is a regular file */
+  if( stat( (const char *)&buf[0], &sb ) == 0 && S_ISREG(sb.st_mode) )
+  {
+    char *ln      = NULL;
+    char *line    = NULL;
+    char *pattern = NULL;
+    int   n = 0;
+
+    FILE *input;
+
+    input = fopen( (const char *)&buf[0], "r" );
+    if( !input )
+    {
+      FATAL_ERROR( "Unable to access %s file", (char *)&buf[0] );
+    }
+
+    line = (char *)malloc( (size_t)PATH_MAX );
+    if( !line )    { FATAL_ERROR( "Cannot allocate memory" ); }
+
+    pattern = (char *)malloc( (size_t)strlen( pkgname ) + 2 );
+    if( !pattern ) { FATAL_ERROR( "Cannot allocate memory" ); }
+
+    (void)sprintf( pattern, "%s:", pkgname );
+
+    /* cat .DESCRIPTION >> PKGLOG */
+    while( (ln = fgets( line, PATH_MAX, input )) )
+    {
+      char *match = NULL;
+
+      ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol      */
+      skip_eol_spaces( ln );     /* remove spaces at end-of-line */
+
+      /*
+        skip non-significant spaces at beginning of line
+        and print lines started with 'pkgname:'
+       */
+      if( (match = strstr( ln, pattern )) && n < DESCRIPTION_NUMBER_OF_LINES )
+      {
+        int mlen   = strlen( match ), plen = strlen( pattern );
+        int length = ( mlen > plen )  ? (mlen - plen - 1) : 0 ;
+
+        if( length > DESCRIPTION_LENGTH_OF_LINE )
+        {
+          /* WARNING( "Package DESCRIPTION contains lines with length greater than %d characters", DESCRIPTION_LENGTH_OF_LINE ); */
+          match[plen + 1 + DESCRIPTION_LENGTH_OF_LINE] = '\0'; /* truncating description line  */
+          skip_eol_spaces( match );                            /* remove spaces at end-of-line */
+        }
+        fprintf( output, "%s\n", match );
+        ++n;
+      }
+    }
+
+    if( n < DESCRIPTION_NUMBER_OF_LINES )
+    {
+      /* WARNING( "Package DESCRIPTION contains less than %d lines", DESCRIPTION_NUMBER_OF_LINES ); */
+      while( n < DESCRIPTION_NUMBER_OF_LINES )
+      {
+        fprintf( output, "%s\n", pattern );
+        ++n;
+      }
+    }
+
+    free( pattern );
+    free( line );
+  }
+
+  free( buf );
+}
+
+
+void write_restore_links( void )
+{
+  struct stat sb;
+  char  *buf = NULL;
+
+  if( output == NULL && output_fname == NULL )
+  {
+    FATAL_ERROR( "Unable to access output file" );
+  }
+
+  buf = (char *)malloc( (size_t)PATH_MAX );
+  if( !buf )
+  {
+    FATAL_ERROR( "Cannot allocate memory" );
+  }
+
+  fprintf( output, "RESTORE LINKS:\n" );
+
+  bzero( (void *)buf, PATH_MAX );
+  (void)sprintf( (char *)&buf[0], "%s/.RESTORELINKS", srcdir );
+
+  /* check if path exists and is a regular file */
+  if( stat( (const char *)&buf[0], &sb ) == 0 && S_ISREG(sb.st_mode) )
+  {
+    char *ln   = NULL;
+    char *line = NULL;
+
+    FILE *input;
+
+    input = fopen( (const char *)&buf[0], "r" );
+    if( !input )
+    {
+      FATAL_ERROR( "Unable to access %s file", (char *)&buf[0] );
+    }
+
+    line = (char *)malloc( (size_t)PATH_MAX );
+    if( !line )
+    {
+      FATAL_ERROR( "Cannot allocate memory" );
+    }
+
+    /* cat .REQUIRES >> PKGLOG */
+    while( (ln = fgets( line, PATH_MAX, input )) )
+    {
+      ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol      */
+      skip_eol_spaces( ln );     /* remove spaces at end-of-line */
+
+      /* print non-empty lines */
+      if( *ln )
+      {
+        fprintf( output, "%s\n", ln );
+      }
+    }
+
+    free( line );
+  }
+
+  free( buf );
+}
+
+
+void write_install_script( void )
+{
+  struct stat sb;
+  char  *buf = NULL;
+
+  if( output == NULL && output_fname == NULL )
+  {
+    FATAL_ERROR( "Unable to access output file" );
+  }
+
+  buf = (char *)malloc( (size_t)PATH_MAX );
+  if( !buf )
+  {
+    FATAL_ERROR( "Cannot allocate memory" );
+  }
+
+  fprintf( output, "INSTALL SCRIPT:\n" );
+
+  bzero( (void *)buf, PATH_MAX );
+  (void)sprintf( (char *)&buf[0], "%s/.INSTALL", srcdir );
+
+  /* check if path exists and is a regular file */
+  if( stat( (const char *)&buf[0], &sb ) == 0 && S_ISREG(sb.st_mode) )
+  {
+    char *ln   = NULL;
+    char *line = NULL;
+
+    FILE *input;
+
+    input = fopen( (const char *)&buf[0], "r" );
+    if( !input )
+    {
+      FATAL_ERROR( "Unable to access %s file", (char *)&buf[0] );
+    }
+
+    line = (char *)malloc( (size_t)PATH_MAX );
+    if( !line )
+    {
+      FATAL_ERROR( "Cannot allocate memory" );
+    }
+
+    /* cat .REQUIRES >> PKGLOG */
+    while( (ln = fgets( line, PATH_MAX, input )) )
+    {
+      ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol      */
+      skip_eol_spaces( ln );     /* remove spaces at end-of-line */
+
+      /* print all lines */
+      fprintf( output, "%s\n", ln );
+    }
+    free( line );
+  }
+  else
+  {
+    FATAL_ERROR( "Package doesn't contains the INSTALL script" );
+  }
+
+  free( buf );
+}
+
+
+void write_file_list( void )
+{
+  struct stat sb;
+  char  *buf = NULL;
+
+  if( output == NULL && output_fname == NULL )
+  {
+    FATAL_ERROR( "Unable to access output file" );
+  }
+
+  buf = (char *)malloc( (size_t)PATH_MAX );
+  if( !buf )
+  {
+    FATAL_ERROR( "Cannot allocate memory" );
+  }
+
+  fprintf( output, "FILE LIST:\n" );
+
+  bzero( (void *)buf, PATH_MAX );
+  (void)sprintf( (char *)&buf[0], "%s/.FILELIST", srcdir );
+
+  /* check if path exists and is a regular file */
+  if( stat( (const char *)&buf[0], &sb ) == 0 && S_ISREG(sb.st_mode) )
+  {
+    char *ln;
+    char *line = NULL;
+
+    FILE *input;
+
+    input = fopen( (const char *)&buf[0], "r" );
+    if( !input )
+    {
+      FATAL_ERROR( "Unable to access %s file", (char *)&buf[0] );
+    }
+
+    line = (char *)malloc( (size_t)PATH_MAX );
+    if( !line )
+    {
+      FATAL_ERROR( "Cannot allocate memory" );
+    }
+
+    /* cat .REQUIRES >> PKGLOG */
+    while( (ln = fgets( line, PATH_MAX, input )) )
+    {
+      ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol      */
+      skip_eol_spaces( ln );     /* remove spaces at end-of-line */
+
+      /* print non-empty lines */
+      if( *ln )
+      {
+        fprintf( output, "%s\n", ln );
+      }
+    }
+
+    free( line );
+  }
+  else
+  {
+    FATAL_ERROR( "Package doesn't contains the FILE list" );
+  }
+
+  free( buf );
+}
+
+
+/*********************************************
+  Get directory where this program is placed:
+ */
+char *get_selfdir( void )
+{
+  char    *buf = NULL;
+  ssize_t  len;
+
+  buf = (char *)malloc( (size_t)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 );
+
+  fatal_error_hook = fatal_error_actions;
+
+  selfdir = get_selfdir();
+
+  errlog = stderr;
+
+  program = basename( argv[0] );
+  get_args( argc, argv );
+
+  /* set_stack_size(); */
+
+  if( pkginfo_type != PKGINFO_TEXT )
+  {
+    /* Create tmpdir */
+    srcdir = _mk_tmpdir();
+    if( !srcdir )
+    {
+      FATAL_ERROR( "Cannot create temporary dir" );
+    }
+    rm_srcdir_at_exit = 1;
+
+    /* Unpack SERVICE files */
+    {
+      pid_t p = (pid_t) -1;
+      int   rc;
+
+      int   len = 0;
+      char *cmd = NULL, *errmsg = NULL, *wmsg = NULL;
+
+      cmd = (char *)malloc( (size_t)PATH_MAX );
+      if( !cmd )    { FATAL_ERROR( "Cannot allocate memory" ); }
+
+      errmsg = (char *)malloc( (size_t)PATH_MAX );
+      if( !errmsg ) { FATAL_ERROR( "Cannot allocate memory" ); }
+
+      wmsg = (char *)malloc( (size_t)PATH_MAX );
+      if( !wmsg )   { FATAL_ERROR( "Cannot allocate memory" ); }
+
+      bzero( (void *)cmd, PATH_MAX );
+      bzero( (void *)errmsg, PATH_MAX );
+      bzero( (void *)wmsg, PATH_MAX );
+
+      (void)sprintf( &errmsg[0], "Cannot get SERVICE files from %s file", basename( pkginfo_fname ) );
+
+      len = snprintf( &cmd[0], PATH_MAX, "tar -C %s -x%sf %s %s > /dev/null 2>&1",
+                                          srcdir, uncompress, pkginfo_fname,
+                                         ".PKGINFO .REQUIRES .DESCRIPTION .RESTORELINKS .INSTALL .FILELIST" );
+      if( len == 0 || len == PATH_MAX - 1 )
+      {
+        FATAL_ERROR( errmsg );
+      }
+      p = sys_exec_command( cmd );
+      rc = sys_wait_command( p, (char *)&wmsg[0], PATH_MAX );
+      if( rc != 0 )
+      {
+        if( ! DO_NOT_WARN_ABOUT_SERVICE_FILES )
+        {
+          /*****************************************
+            if( rc > 0 ) { return TAR exit status }
+            else         { return EXIT_FAILURE    }
+           */
+          if( rc > 0 ) exit_status = rc - 1; /* ERROR() will add one */
+          ERROR( errmsg );
+          if( fatal_error_hook) fatal_error_hook();
+          exit( exit_status );
+        }
+      }
+
+      if( cmd )    free( cmd );
+      if( errmsg ) free( errmsg );
+      if( wmsg )   free( wmsg );
+    }
+
+    /* Change input pkginfo file name and type */
+    if( pkginfo_fname )
+    {
+      char *buf = NULL;
+
+      buf = (char *)malloc( strlen( pkginfo_fname ) + 10 );
+      if( !buf )
+      {
+        FATAL_ERROR( "Cannot allocate memory" );
+      }
+
+      (void)sprintf( &buf[0], "%s/.PKGINFO", srcdir );
+
+      free( pkginfo_fname ); pkginfo_fname = NULL;
+
+      pkginfo_fname = xstrdup( (const char *)buf );
+      free( buf );
+      pkginfo_type  = PKGINFO_TEXT;
+    }
+
+  }
+  else /* TEXT: */
+  {
+    char *buf = NULL;
+
+    buf = (char *)malloc( (size_t)strlen( pkginfo_fname ) + 1 );
+    if( !buf )
+    {
+      FATAL_ERROR( "Cannot allocate memory" );
+    }
+
+    /* function dirname() spoils the source contents: */
+    (void)sprintf( buf, "%s", pkginfo_fname );
+
+    srcdir = xstrdup( (const char *)dirname( (char *)&buf[0] ) );
+    free( buf );
+    rm_srcdir_at_exit = 0;
+  }
+
+
+  write_pkginfo();
+  write_requires();
+  write_description();
+  write_restore_links();
+  write_install_script();
+  write_file_list();
+
+
+  if( rm_srcdir_at_exit ) _rm_tmpdir( (const char *)srcdir );
+  free_resources();
+
+  exit( exit_status );
+}