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
     5         kx 
     5         kx /**********************************************************************
     5         kx 
     5         kx   Copyright 2019 Andrey V.Kosteltsev
     5         kx 
     5         kx   Licensed under the Radix.pro License, Version 1.0 (the "License");
     5         kx   you may not use this file  except  in compliance with the License.
     5         kx   You may obtain a copy of the License at
     5         kx 
     5         kx      https://radix.pro/licenses/LICENSE-1.0-en_US.txt
     5         kx 
     5         kx   Unless required by applicable law or agreed to in writing, software
     5         kx   distributed under the License is distributed on an "AS IS" BASIS,
     5         kx   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
     5         kx   implied.
     5         kx 
     5         kx  **********************************************************************/
     5         kx 
     5         kx #include <config.h>
     5         kx 
     5         kx #include <stdlib.h>
     5         kx #include <stdio.h>
     5         kx #include <sys/sysinfo.h>
     5         kx #include <sys/types.h>
     5         kx #include <stdint.h>
     5         kx #include <dirent.h>
     5         kx #include <sys/stat.h> /* chmod(2)    */
     5         kx #include <fcntl.h>
     5         kx #include <linux/limits.h>
     5         kx #include <alloca.h>   /* alloca(3)   */
     5         kx #include <string.h>   /* strdup(3)   */
     5         kx #include <libgen.h>   /* basename(3) */
     5         kx #include <ctype.h>    /* tolower(3)  */
     5         kx #include <errno.h>
     5         kx #include <time.h>
     5         kx #include <sys/time.h>
     5         kx #include <pwd.h>
     5         kx #include <grp.h>
     5         kx #include <stdarg.h>
     5         kx #include <unistd.h>
     5         kx 
     5         kx #include <math.h>
     5         kx 
     5         kx #include <sys/resource.h>
     5         kx 
     5         kx #include <signal.h>
     5         kx #if !defined SIGCHLD && defined SIGCLD
     5         kx # define SIGCHLD SIGCLD
     5         kx #endif
     5         kx 
     5         kx #define _GNU_SOURCE
     5         kx #include <getopt.h>
     5         kx 
     5         kx #include <msglog.h>
     5         kx #include <wrapper.h>
     5         kx #include <system.h>
     5         kx #include <dlist.h>
     5         kx 
     5         kx #define PROGRAM_NAME "make-package"
     5         kx 
     5         kx #include <defs.h>
     5         kx 
     5         kx 
     5         kx char *program = PROGRAM_NAME;
     5         kx char *destination = NULL, *srcdir = NULL, *flavour = NULL,
     5         kx      *tmpdir = NULL;
     5         kx 
     5         kx FILE   *rlinks  = NULL;
     5         kx size_t  pkgsize = 0;
     5         kx int     nfiles  = 0;
     5         kx 
     5         kx const char *txz_suffix = ".txz";
     5         kx char  compress = 'J';
     5         kx 
     5         kx #if defined( HAVE_GPG2 )
     5         kx char *gnupghome = NULL, *passphrase = NULL, *key_id = NULL;
     5         kx #endif
     5         kx 
     5         kx int   exit_status = EXIT_SUCCESS; /* errors counter */
     5         kx char *selfdir     = NULL;
     5         kx 
     5         kx int   mkgroupdir  = 0;
     5         kx int   linkadd     = 1;
     5         kx 
     5         kx static char           *pkgname = NULL,
     5         kx                        *pkgver = NULL,
     5         kx                          *arch = NULL,
     5         kx                    *distroname = NULL,
     5         kx                     *distrover = NULL,
     5         kx                         *group = NULL,
     5         kx             *short_description = NULL,
     5         kx                           *url = NULL,
     5         kx                       *license = NULL,
     5         kx             *uncompressed_size = NULL,
     5         kx                   *total_files = NULL;
     5         kx 
     5         kx struct dlist *filelist = NULL;
     5         kx 
     5         kx static void create_file_list( void );
     5         kx static void free_file_list( void );
     5         kx 
     5         kx 
     5         kx #define FREE_PKGINFO_VARIABLES() \
     5         kx   if( pkgname )           { free( pkgname );           } pkgname = NULL;            \
     5         kx   if( pkgver )            { free( pkgver );            } pkgver = NULL;             \
     5         kx   if( arch )              { free( arch );              } arch = NULL;               \
     5         kx   if( distroname )        { free( distroname );        } distroname = NULL;         \
     5         kx   if( distrover )         { free( distrover );         } distrover = NULL;          \
     5         kx   if( group )             { free( group );             } group = NULL;              \
     5         kx   if( short_description ) { free( short_description ); } short_description = NULL;  \
     5         kx   if( url )               { free( url );               } url = NULL;                \
     5         kx   if( license )           { free( license );           } license = NULL;            \
     5         kx   if( uncompressed_size ) { free( uncompressed_size ); } uncompressed_size = NULL;  \
     5         kx   if( total_files )       { free( total_files );       } total_files = NULL
     5         kx 
     5         kx void free_resources()
     5         kx {
     5         kx   if( srcdir )        { free( srcdir );        srcdir        = NULL; }
     5         kx   if( destination )   { free( destination );   destination   = NULL; }
     5         kx   if( flavour )       { free( flavour );       flavour       = NULL; }
     5         kx   if( filelist )      { free_file_list();      filelist      = NULL; }
     5         kx 
     5         kx #if defined( HAVE_GPG2 )
     5         kx   if( gnupghome )     { free( gnupghome );     gnupghome     = NULL; }
     5         kx   if( passphrase )    { free( passphrase );    passphrase    = NULL; }
     5         kx   if( key_id )        { free( key_id );        key_id        = NULL; }
     5         kx #endif
     5         kx 
     5         kx   if( selfdir )       { free( selfdir );       selfdir       = NULL; }
     5         kx 
     5         kx   FREE_PKGINFO_VARIABLES();
     5         kx }
     5         kx 
     5         kx void usage()
     5         kx {
     5         kx   free_resources();
     5         kx 
     5         kx   fprintf( stdout, "\n" );
     5         kx   fprintf( stdout, "Usage: %s [options] <srcpkgdir>\n", program );
     5         kx   fprintf( stdout, "\n" );
     5         kx   fprintf( stdout, "Create PACKAGE from SRCPKGDIR where package is installed. The source\n" );
     5         kx   fprintf( stdout, "directory should content the package service files:\n" );
     5         kx   fprintf( stdout, "\n" );
     5         kx   fprintf( stdout, "  .PKGINFO, .INSTALL, .DESCRIPTION, and .REQUIRES (if applicable)\n" );
     5         kx   fprintf( stdout, "\n" );
     5         kx   fprintf( stdout, "The '.PKGINFO' file is obligatory and should content declarations of\n" );
     5         kx   fprintf( stdout, "following variables:  pkgname, pkgver, arch,  distroname, distrover.\n" );
     5         kx   fprintf( stdout, "Also in the .PKGINFO file  can be defined additional and recommended\n" );
     5         kx   fprintf( stdout, "variables: group, short_description, url, license.\n" );
     5         kx   fprintf( stdout, "\n" );
     5         kx   fprintf( stdout, "Options:\n" );
     5         kx   fprintf( stdout, "  -h,--help                  Display this information.\n" );
     5         kx   fprintf( stdout, "  -v,--version               Display the version of %s utility.\n", program );
     5         kx   fprintf( stdout, "  -d,--destination=<DIR>     Target directory to save output PACKAGE.\n" );
     5         kx   fprintf( stdout, "  -m,--mkgroupdir            Create GROUP subdirectory in the PACKAGE\n" );
     5         kx   fprintf( stdout, "                             target directory.\n" );
     5         kx   fprintf( stdout, "  -l,--linkadd={y|n}         Create .RESTORELINKS scrypt (default yes).\n" );
     5         kx   fprintf( stdout, "  -f,--flavour=<subdir>      The name of additional subdirectory in the\n" );
     5         kx   fprintf( stdout, "                             GROUP directory to save target PACKAGE.\n" );
     5         kx   fprintf( stdout, "\n" );
     5         kx #if defined( HAVE_GPG2 )
     5         kx   fprintf( stdout, "OpenPGP options:\n" );
     5         kx   fprintf( stdout, "  -g,--gnupghome=<DIR>       Set the name of the GnuPG home directory\n" );
     5         kx   fprintf( stdout, "                             to <DIR>. If this option is not used it\n" );
     5         kx   fprintf( stdout, "                             defaults to '~/.gnupg'. This also overrides\n" );
     5         kx   fprintf( stdout, "                             the environment variable 'GNUPGHOME'.\n" );
     5         kx   fprintf( stdout, "  -p,--passphrase=<FILE>     File with passphrase of private certificate\n" );
     5         kx   fprintf( stdout, "                             for signing package. For example:\n" );
     5         kx   fprintf( stdout, "                                ~/.gnupg/.passphrase\n" );
     5         kx   fprintf( stdout, "                             Passphrase should be placed in the first\n" );
     5         kx   fprintf( stdout, "                             line of the file (the new-line symbol at\n" );
     5         kx   fprintf( stdout, "                             end of passphrase is allowed). File must\n" );
     5         kx   fprintf( stdout, "                             have access mode 600.\n" );
     5         kx   fprintf( stdout, "  -k,--key-id=<USER-ID>      Use USER-ID to sign package, for example,\n" );
     5         kx   fprintf( stdout, "                             --key-id=0xA5ED710298807270\n" );
     5         kx   fprintf( stdout, "\n" );
     5         kx #endif
     5         kx   fprintf( stdout, "Compression options:\n" );
     5         kx   fprintf( stdout, "  -J,--xz                    Filter the package archive through xz(1).\n" );
     5         kx   fprintf( stdout, "  -j,--bzip2                 Filter the package archive through bzip2(1).\n" );
     5         kx   fprintf( stdout, "  -z,--gzip                  Filter the package archive through gzip(1).\n" );
     5         kx   fprintf( stdout, "\n" );
     5         kx #if defined( HAVE_GPG2 )
     5         kx   fprintf( stdout, "  If one of arguments:  passphrase or key-id  is not specified, then\n" );
     5         kx   fprintf( stdout, "  signature will not be created and utility doesn't return any error\n" );
     5         kx   fprintf( stdout, "  code.  If error occurs during the creation of a package signature,\n" );
     5         kx   fprintf( stdout, "  the utility returns error code, but continue to create the package\n" );
     5         kx   fprintf( stdout, "  (in this case the signature will not be created).\n" );
     5         kx   fprintf( stdout, "\n" );
     5         kx #endif
     5         kx   fprintf( stdout, "Parameter:\n" );
     5         kx   fprintf( stdout, "  <srcpkgdir>                Directory wich contains source package\n"  );
     5         kx   fprintf( stdout, "                             and package service files.\n"  );
     5         kx   fprintf( stdout, "\n" );
     5         kx 
     5         kx   exit( EXIT_FAILURE );
     5         kx }
     5         kx 
     5         kx void to_lowercase( char *s )
     5         kx {
     5         kx   char *p = s;
     5         kx   while( p && *p ) { int c = *p; *p = tolower( c ); ++p; }
     5         kx }
     5         kx 
     5         kx void to_uppercase( char *s )
     5         kx {
     5         kx   char *p = s;
     5         kx   while( p && *p ) { int c = *p; *p = toupper( c ); ++p; }
     5         kx }
     5         kx 
     5         kx void version()
     5         kx {
     5         kx   char *upper = NULL;
     5         kx 
     5         kx   upper = (char *)alloca( strlen( program ) + 1 );
     5         kx 
     5         kx   strcpy( (char *)upper, (const char *)program );
     5         kx   to_uppercase( upper );
     5         kx 
     5         kx   fprintf( stdout, "%s (%s) %s\n", program, upper, PROGRAM_VERSION );
     5         kx 
     5         kx   fprintf( stdout, "Copyright (C) 2019 Andrey V.Kosteltsev.\n" );
     5         kx   fprintf( stdout, "This is free software.   There is NO warranty; not even\n" );
     5         kx   fprintf( stdout, "for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n" );
     5         kx   fprintf( stdout, "\n" );
     5         kx 
     5         kx   free_resources();
     5         kx   exit( EXIT_SUCCESS );
     5         kx }
     5         kx 
     5         kx 
     5         kx static void remove_trailing_slash( char *dir )
     5         kx {
     5         kx   char *s;
     5         kx 
     5         kx   if( !dir || dir[0] == '\0' ) return;
     5         kx 
     5         kx   s = dir + strlen( dir ) - 1;
     5         kx   while( *s == '/' )
     5         kx   {
     5         kx     *s = '\0'; --s;
     5         kx   }
     5         kx }
     5         kx 
     5         kx 
     5         kx static int _mkdir_p( const char *dir, const mode_t mode )
     5         kx {
     5         kx   char  *buf;
     5         kx   char  *p = NULL;
     5         kx   struct stat sb;
     5         kx 
     5         kx   if( !dir ) return -1;
     5         kx 
     5         kx   buf = (char *)alloca( strlen( dir ) + 1 );
     5         kx   strcpy( buf, dir );
     5         kx 
     5         kx   remove_trailing_slash( buf );
     5         kx 
     5         kx   /* check if path exists and is a directory */
     5         kx   if( stat( buf, &sb ) == 0 )
     5         kx   {
     5         kx     if( S_ISDIR(sb.st_mode) )
     5         kx     {
     5         kx       return 0;
     5         kx     }
     5         kx   }
     5         kx 
     5         kx   /* mkdir -p */
     5         kx   for( p = buf + 1; *p; ++p )
     5         kx   {
     5         kx     if( *p == '/' )
     5         kx     {
     5         kx       *p = 0;
     5         kx       /* test path */
     5         kx       if( stat( buf, &sb ) != 0 )
     5         kx       {
     5         kx         /* path does not exist - create directory */
     5         kx         if( mkdir( buf, mode ) < 0 )
     5         kx         {
     5         kx           return -1;
     5         kx         }
     5         kx       } else if( !S_ISDIR(sb.st_mode) )
     5         kx       {
     5         kx         /* not a directory */
     5         kx         return -1;
     5         kx       }
     5         kx       *p = '/';
     5         kx     }
     5         kx   }
     5         kx 
     5         kx   /* test path */
     5         kx   if( stat( buf, &sb ) != 0 )
     5         kx   {
     5         kx     /* path does not exist - create directory */
     5         kx     if( mkdir( buf, mode ) < 0 )
     5         kx     {
     5         kx       return -1;
     5         kx     }
     5         kx   } else if( !S_ISDIR(sb.st_mode) )
     5         kx   {
     5         kx     /* not a directory */
     5         kx     return -1;
     5         kx   }
     5         kx 
     5         kx   return 0;
     5         kx }
     5         kx 
     5         kx static void _rm_tmpdir( const char *dirpath )
     5         kx {
     5         kx   DIR    *dir;
     5         kx   char   *path;
     5         kx   size_t  len;
     5         kx 
     5         kx   struct stat    path_sb, entry_sb;
     5         kx   struct dirent *entry;
     5         kx 
     5         kx   if( stat( dirpath, &path_sb ) == -1 )
     5         kx   {
     5         kx     return; /* stat returns error code; errno is set */
     5         kx   }
     5         kx 
     5         kx   if( S_ISDIR(path_sb.st_mode) == 0 )
     5         kx   {
     5         kx     return; /* dirpath is not a directory */
     5         kx   }
     5         kx 
     5         kx   if( (dir = opendir(dirpath) ) == NULL )
     5         kx   {
     5         kx     return; /* Cannot open direcroty; errno is set */
     5         kx   }
     5         kx 
     5         kx   len = strlen( dirpath );
     5         kx 
     5         kx   while( (entry = readdir( dir )) != NULL)
     5         kx   {
     5         kx 
     5         kx     /* skip entries '.' and '..' */
     5         kx     if( ! strcmp( entry->d_name, "." ) || ! strcmp( entry->d_name, ".." ) ) continue;
     5         kx 
     5         kx     /* determinate a full name of an entry */
     5         kx     path = alloca( len + strlen( entry->d_name ) + 2 );
     5         kx     strcpy( path, dirpath );
     5         kx     strcat( path, "/" );
     5         kx     strcat( path, entry->d_name );
     5         kx 
     5         kx     if( stat( path, &entry_sb ) == 0 )
     5         kx     {
     5         kx       if( S_ISDIR(entry_sb.st_mode) )
     5         kx       {
     5         kx         /* recursively remove a nested directory */
     5         kx         _rm_tmpdir( path );
     5         kx       }
     5         kx       else
     5         kx       {
     5         kx         /* remove a file object */
     5         kx         (void)unlink( path );
     5         kx       }
     5         kx     }
     5         kx     /* else { stat() returns error code; errno is set; and we have to continue the loop } */
     5         kx 
     5         kx   }
     5         kx 
     5         kx   /* remove the devastated directory and close the object of this directory */
     5         kx   (void)rmdir( dirpath );
     5         kx 
     5         kx   closedir( dir );
     5         kx }
     5         kx 
     5         kx static char *_mk_tmpdir( void )
     5         kx {
     5         kx   char   *buf = NULL, *p, *tmp = "/tmp";
     5         kx   size_t  len = 0, size = 0;
     5         kx 
     5         kx   (void)umask( S_IWGRP | S_IWOTH ); /* octal 022 */
     5         kx 
     5         kx   /* Get preferred directory for tmp files */
     5         kx   if( (p = getenv( "TMP" )) != NULL ) {
     5         kx     tmp = p;
     5         kx   }
     5         kx   else if( (p = getenv( "TEMP" )) != NULL ) {
     5         kx     tmp = p;
     5         kx   }
     5         kx 
     5         kx   size = strlen( tmp ) + strlen( DISTRO_NAME ) + strlen( program ) + 12;
     5         kx 
     5         kx   buf = (char *)malloc( size );
     5         kx   if( !buf ) return NULL;
     5         kx 
     5         kx   len = snprintf( buf, size, (const char *)"%s/%s/%s-%.7u", tmp, DISTRO_NAME, program, getpid() );
     5         kx   if( len == 0 || len == size - 1 )
     5         kx   {
     5         kx     free( buf ); return NULL;
     5         kx   }
     5         kx 
     5         kx   _rm_tmpdir( (const char *)&buf[0] );
     5         kx 
     5         kx   if( _mkdir_p( buf, S_IRWXU | S_IRWXG | S_IRWXO ) == 0 )
     5         kx   {
     5         kx     return buf;
     5         kx   }
     5         kx 
     5         kx   free( buf ); return NULL;
     5         kx }
     5         kx 
     5         kx 
     5         kx void fatal_error_actions( void )
     5         kx {
     5         kx   logmsg( errlog, MSG_NOTICE, "Free resources on FATAL error..." );
     5         kx   if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); }
     5         kx   free_resources();
     5         kx }
     5         kx 
     5         kx void sigint( int signum )
     5         kx {
     5         kx   (void)signum;
     5         kx 
     5         kx   if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); }
     5         kx   free_resources();
     5         kx }
     5         kx 
     5         kx static void set_signal_handlers()
     5         kx {
     5         kx   struct sigaction  sa;
     5         kx   sigset_t          set;
     5         kx 
     5         kx   memset( &sa, 0, sizeof( sa ) );
     5         kx   sa.sa_handler = sigint;          /* TERM, INT */
     5         kx   sa.sa_flags = SA_RESTART;
     5         kx   sigemptyset( &set );
     5         kx   sigaddset( &set, SIGTERM );
     5         kx   sigaddset( &set, SIGINT );
     5         kx   sa.sa_mask = set;
     5         kx   sigaction( SIGTERM, &sa, NULL );
     5         kx   sigaction( SIGINT, &sa,  NULL );
     5         kx 
     5         kx   memset( &sa, 0, sizeof( sa ) );  /* ignore SIGPIPE */
     5         kx   sa.sa_handler = SIG_IGN;
     5         kx   sa.sa_flags = 0;
     5         kx   sigaction( SIGPIPE, &sa, NULL );
     5         kx 
     5         kx   /* System V fork+wait does not work if SIGCHLD is ignored */
     5         kx   signal( SIGCHLD, SIG_DFL );
     5         kx }
     5         kx 
     5         kx 
     5         kx 
     5         kx void get_args( int argc, char *argv[] )
     5         kx {
     5         kx #if defined( HAVE_GPG2 )
     5         kx   const char* short_options = "hvmd:l:f:g:p:k:Jjz";
     5         kx #else
     5         kx   const char* short_options = "hvmd:l:f:Jjz";
     5         kx #endif
     5         kx 
     5         kx   const struct option long_options[] =
     5         kx   {
     5         kx     { "help",        no_argument,       NULL, 'h' },
     5         kx     { "version",     no_argument,       NULL, 'v' },
     5         kx     { "destination", required_argument, NULL, 'd' },
     5         kx     { "mkgroupdir",  no_argument,       NULL, 'm' },
     5         kx     { "linkadd",     required_argument, NULL, 'l' },
     5         kx     { "flavour",     required_argument, NULL, 'f' },
     5         kx #if defined( HAVE_GPG2 )
     5         kx     { "gnupghome",   required_argument, NULL, 'g' },
     5         kx     { "passphrase",  required_argument, NULL, 'p' },
     5         kx     { "key-id",      required_argument, NULL, 'k' },
     5         kx #endif
     5         kx     { "xz",          no_argument,       NULL, 'J' },
     5         kx     { "bzip2",       no_argument,       NULL, 'j' },
     5         kx     { "gzip",        no_argument,       NULL, 'z' },
     5         kx     { NULL,          0,                 NULL,  0  }
     5         kx   };
     5         kx 
     5         kx   int ret;
     5         kx   int option_index = 0;
     5         kx 
     5         kx   while( (ret = getopt_long( argc, argv, short_options, long_options, &option_index )) != -1 )
     5         kx   {
     5         kx     switch( ret )
     5         kx     {
     5         kx       case 'h':
     5         kx       {
     5         kx         usage();
     5         kx         break;
     5         kx       }
     5         kx       case 'v':
     5         kx       {
     5         kx         version();
     5         kx         break;
     5         kx       }
     5         kx 
     5         kx       case 'd':
     5         kx       {
     5         kx         if( optarg != NULL )
     5         kx         {
     5         kx           destination = xstrdup( (const char *)optarg );
     5         kx           remove_trailing_slash( destination );
     5         kx         }
     5         kx         else
     5         kx           /* option is present but without value */
     5         kx           usage();
     5         kx         break;
     5         kx       }
     5         kx       case 'm':
     5         kx       {
     5         kx         mkgroupdir = 1;
     5         kx         break;
     5         kx       }
     5         kx 
     5         kx       case 'J':
     5         kx       {
     5         kx         compress = 'J';
     5         kx         txz_suffix = ".txz";
     5         kx         break;
     5         kx       }
     5         kx       case 'j':
     5         kx       {
     5         kx         compress = 'j';
     5         kx         txz_suffix = ".tbz";
     5         kx         break;
     5         kx       }
     5         kx       case 'z':
     5         kx       {
     5         kx         compress = 'z';
     5         kx         txz_suffix = ".tgz";
     5         kx         break;
     5         kx       }
     5         kx 
     5         kx       case 'l':
     5         kx       {
     5         kx         if( optarg != NULL )
     5         kx         {
     5         kx           char  *buf = NULL;
     5         kx           size_t len = strlen( optarg ) + 1;
     5         kx 
     5         kx           buf = (char *)malloc( len );
     5         kx           if( !buf ) { FATAL_ERROR( "Cannot allocate memory" ); }
     5         kx           bzero( (void *)buf, len );
     5         kx 
     5         kx           (void)strcpy( buf, (const char *)optarg );
     5         kx           to_lowercase( buf );
     5         kx           if( !strncmp( (const char *)&buf[0], "n", 1 ) )
     5         kx           {
     5         kx             linkadd = 0;
     5         kx           }
     5         kx           free( buf );
     5         kx         }
     5         kx         else
     5         kx           /* option is present but without value */
     5         kx           usage();
     5         kx         break;
     5         kx       }
     5         kx       case 'f':
     5         kx       {
     5         kx         if( optarg != NULL )
     5         kx         {
     5         kx           char  *buf = NULL;
     5         kx           size_t len = strlen( optarg ) + 1;
     5         kx 
     5         kx           buf = (char *)malloc( len );
     5         kx           if( !buf ) { FATAL_ERROR( "Cannot allocate memory" ); }
     5         kx           bzero( (void *)buf, len );
     5         kx 
     5         kx           (void)strcpy( buf, (const char *)optarg );
     5         kx           to_lowercase( buf );
     5         kx 
     5         kx           flavour = xstrdup( (const char *)&buf[0] );
     5         kx           free( buf );
     5         kx         }
     5         kx         else
     5         kx           /* option is present but without value */
     5         kx           usage();
     5         kx         break;
     5         kx       }
     5         kx 
     5         kx #if defined( HAVE_GPG2 )
     5         kx       case 'g':
     5         kx       {
     5         kx         if( optarg != NULL )
     5         kx         {
     5         kx           struct stat st;
     5         kx           char  *buf  = NULL;
     5         kx           char  *home = NULL;
     5         kx 
     5         kx           bzero( (void *)&st, sizeof( struct stat ) );
     5         kx 
     5         kx           buf = (char *)malloc( (size_t)PATH_MAX );
     5         kx           if( !buf ) { FATAL_ERROR( "Cannot allocate memory" ); }
     5         kx           bzero( (void *)buf, PATH_MAX );
     5         kx 
     5         kx           if( *optarg == '~' )
     5         kx           {
     5         kx             home = getenv( "HOME" );
     5         kx             if( home )
     5         kx             {
     5         kx               (void)sprintf( buf, "%s/%s", home, (const char *)((char *)optarg + 2) );
     5         kx             }
     5         kx             else
     5         kx             {
     5         kx               FATAL_ERROR( "Cannot get HOME directory" );
     5         kx             }
     5         kx           }
     5         kx           else
     5         kx           {
     5         kx             (void)strcpy( buf, (const char *)optarg );
     5         kx           }
     5         kx 
     5         kx           if( stat( (const char *)&buf[0], &st ) == -1 )
     5         kx           {
     5         kx             FATAL_ERROR( "Cannot access '%s' GnuPG home directory: %s", buf, strerror( errno ) );
     5         kx           }
     5         kx           if( !S_ISDIR(st.st_mode) )
     5         kx           {
     5         kx             FATAL_ERROR( "The GNUPGHOME '%s' is not a directory", buf );
     5         kx           }
     5         kx 
     5         kx           remove_trailing_slash( buf );
     5         kx           gnupghome = xstrdup( (const char *)&buf[0] );
     5         kx           free( buf );
     5         kx         }
     5         kx         else
     5         kx           /* option is present but without value */
     5         kx           usage();
     5         kx         break;
     5         kx       }
     5         kx       case 'p':
     5         kx       {
     5         kx         if( optarg != NULL )
     5         kx         {
     5         kx           struct stat st;
     5         kx           char  *buf  = NULL;
     5         kx           char  *home = NULL;
     5         kx 
     5         kx           bzero( (void *)&st, sizeof( struct stat ) );
     5         kx 
     5         kx           buf = (char *)malloc( (size_t)PATH_MAX );
     5         kx           if( !buf ) { FATAL_ERROR( "Cannot allocate memory" ); }
     5         kx           bzero( (void *)buf, PATH_MAX );
     5         kx 
     5         kx           if( *optarg == '~' )
     5         kx           {
     5         kx             home = getenv( "HOME" );
     5         kx             if( home )
     5         kx             {
     5         kx               (void)sprintf( buf, "%s/%s", home, (const char *)((char *)optarg + 2) );
     5         kx             }
     5         kx             else
     5         kx             {
     5         kx               FATAL_ERROR( "Cannot get HOME directory" );
     5         kx             }
     5         kx           }
     5         kx           else
     5         kx           {
     5         kx             (void)strcpy( buf, (const char *)optarg );
     5         kx           }
     5         kx 
     5         kx           if( stat( (const char *)&buf[0], &st ) == -1 )
     5         kx           {
     5         kx             FATAL_ERROR( "Cannot access '%s' passphrase source file: %s", basename( buf ), strerror( errno ) );
     5         kx           }
     5         kx           if( !S_ISREG(st.st_mode) )
     5         kx           {
     5         kx             FATAL_ERROR( "The passphrase '%s' is not a regular file", basename( buf ) );
     5         kx           }
     5         kx 
     5         kx           passphrase = xstrdup( (const char *)&buf[0] );
     5         kx           free( buf );
     5         kx         }
     5         kx         else
     5         kx           /* option is present but without value */
     5         kx           usage();
     5         kx         break;
     5         kx       }
     5         kx       case 'k':
     5         kx       {
     5         kx         if( optarg != NULL )
     5         kx         {
     5         kx           key_id = xstrdup( (const char *)optarg );
     5         kx         }
     5         kx         else
     5         kx           /* option is present but without value */
     5         kx           usage();
     5         kx         break;
     5         kx       }
     5         kx #endif
     5         kx 
     5         kx       case '?': default:
     5         kx       {
     5         kx         usage();
     5         kx         break;
     5         kx       }
     5         kx     }
     5         kx   }
     5         kx 
     5         kx   /* last command line argument is the PACKAGE source directory */
     5         kx   if( optind < argc )
     5         kx   {
     5         kx     struct stat st;
     5         kx     char  *buf = NULL;
     5         kx 
     5         kx     bzero( (void *)&st, sizeof( struct stat ) );
     5         kx 
     5         kx     buf = (char *)malloc( (size_t)PATH_MAX );
     5         kx     if( !buf ) { FATAL_ERROR( "Cannot allocate memory" ); }
     5         kx     bzero( (void *)buf, PATH_MAX );
     5         kx 
     5         kx     (void)strcpy( buf, (const char *)argv[optind] );
     5         kx     remove_trailing_slash( (char *)&buf[0] );
     5         kx 
     5         kx     if( stat( (const char *)&buf[0], &st ) == -1 )
     5         kx     {
     5         kx       FATAL_ERROR( "Cannot access '%s' PACKAGE source directory: %s", basename( buf ), strerror( errno ) );
     5         kx     }
     5         kx 
     5         kx     if( ! S_ISDIR(st.st_mode) )
     5         kx     {
     5         kx       FATAL_ERROR( "The PACKAGE source '%s' is not a directory", basename( buf ) );
     5         kx     }
     5         kx 
     5         kx     /* Add .PKGINFO to the input dir name: */
     5         kx     (void)strcat( buf, "/.PKGINFO" );
     5         kx     if( stat( (const char *)&buf[0], &st ) == -1 ) {
     5         kx       FATAL_ERROR( "The defined SRCPKGDIR doesn't contain a valid package" );
     5         kx     }
     5         kx     *(strstr( buf, "/.PKGINFO" )) = '\0'; /* restore tmpdir in tmp[] buffer */
     5         kx 
     5         kx     /* Add .DESCRIPTION to the input dir name: */
     5         kx     (void)strcat( buf, "/.DESCRIPTION" );
     5         kx     if( stat( (const char *)&buf[0], &st ) == -1 ) {
     5         kx       FATAL_ERROR( "The defined SRCPKGDIR doesn't contain package '.DESCRIPTION' file" );
     5         kx     }
     5         kx     *(strstr( buf, "/.DESCRIPTION" )) = '\0'; /* restore tmpdir in tmp[] buffer */
     5         kx 
     5         kx     /* Add .INSTALL to the input dir name: */
     5         kx     (void)strcat( buf, "/.INSTALL" );
     5         kx     if( stat( (const char *)&buf[0], &st ) == -1 ) {
     5         kx       FATAL_ERROR( "The defined SRCPKGDIR doesn't contain package '.INSTALL' script" );
     5         kx     }
     5         kx     *(strstr( buf, "/.INSTALL" )) = '\0'; /* restore tmpdir in tmp[] buffer */
     5         kx 
     5         kx 
     5         kx     srcdir = xstrdup( (const char *)&buf[0] );
     5         kx     if( srcdir == NULL )
     5         kx     {
     5         kx       usage();
     5         kx     }
     5         kx 
     5         kx     free( buf );
     5         kx   }
     5         kx   else
     5         kx   {
     5         kx     usage();
     5         kx   }
     5         kx 
     5         kx   if( destination == NULL )
     5         kx   {
     5         kx     char *buf = NULL;
     5         kx 
     5         kx     buf = (char *)malloc( (size_t)PATH_MAX );
     5         kx     if( !buf ) { FATAL_ERROR( "Cannot allocate memory" ); }
     5         kx     bzero( (void *)buf, PATH_MAX );
     5         kx 
     5         kx     (void)strcpy( buf, (const char *)srcdir );
     5         kx     remove_trailing_slash( (char *)&buf[0] );
     5         kx 
     5         kx     destination = xstrdup( (const char *)dirname( (char *)&buf[0] ) );
     5         kx 
     5         kx     free( buf );
     5         kx   }
     5         kx }
     5         kx 
     5         kx 
     5         kx /*
     5         kx   Especialy for pkginfo lines.
     5         kx   Remove leading spaces and take non-space characters only:
     5         kx  */
     5         kx static char *skip_spaces( char *s )
     5         kx {
     5         kx   char *q, *p = (char *)0;
     5         kx 
     5         kx   if( !s || *s == '\0' ) return p;
     5         kx 
     5         kx   p = s;
     5         kx 
     5         kx   while( (*p == ' ' || *p == '\t') && *p != '\0' ) { ++p; } q = p;
     5         kx   while(  *q != ' ' && *q != '\t'  && *q != '\0' ) { ++q; } *q = '\0';
     5         kx 
     5         kx   if( *p == '\0' ) return (char *)0;
     5         kx 
     5         kx   return( xstrdup( (const char *)p ) );
     5         kx }
     5         kx 
     5         kx /*
     5         kx   remove spaces at end of line:
     5         kx  */
     5         kx static void skip_eol_spaces( char *s )
     5         kx {
     5         kx   char *p = (char *)0;
     5         kx 
     5         kx   if( !s || *s == '\0' ) return;
     5         kx 
     5         kx   p = s + strlen( s ) - 1;
     5         kx   while( isspace( *p ) ) { *p-- = '\0'; }
     5         kx }
     5         kx 
     5         kx 
     5         kx static void read_pkginfo( void )
     5         kx {
     5         kx   struct stat st;
     5         kx   char  *buf = NULL;
     5         kx   FILE  *pkginfo;
     5         kx 
     5         kx   bzero( (void *)&st, sizeof( struct stat ) );
     5         kx 
     5         kx   buf = (char *)malloc( (size_t)PATH_MAX );
     5         kx   if( !buf ) { FATAL_ERROR( "Cannot allocate memory" ); }
     5         kx   bzero( (void *)buf, PATH_MAX );
     5         kx 
     5         kx   (void)strcpy( buf, (const char *)srcdir );
     5         kx   (void)strcat( buf, "/.PKGINFO" );
     5         kx 
     5         kx   if( stat( (const char *)&buf[0], &st ) == -1 )
     5         kx   {
     5         kx     FATAL_ERROR( "Cannot access .PKGINFO file: %s", strerror( errno ) );
     5         kx   }
     5         kx   if( !S_ISREG(st.st_mode) )
     5         kx   {
     5         kx     FATAL_ERROR( "The '%s' is not a regular file", basename( buf ) );
     5         kx   }
     5         kx 
     5         kx   pkginfo = fopen( (const char *)&buf[0], "r" );
     5         kx   if( !pkginfo )
     5         kx   {
     5         kx     FATAL_ERROR( "Cannot open '%s' file", basename( buf ) );
     5         kx   }
     5         kx 
     5         kx   {
     5         kx     char *ln   = NULL;
     5         kx     char *line = NULL;
     5         kx 
     5         kx     line = (char *)malloc( (size_t)PATH_MAX );
     5         kx     if( !line )
     5         kx     {
     5         kx       FATAL_ERROR( "Cannot allocate memory" );
     5         kx     }
     5         kx   
     5         kx     while( (ln = fgets( line, PATH_MAX, pkginfo )) )
     5         kx     {
     5         kx       char *match = NULL;
     5         kx 
     5         kx       ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol      */
     5         kx       skip_eol_spaces( ln );     /* remove spaces at end-of-line */
     5         kx 
     5         kx       if( (match = strstr( ln, "pkgname" )) && match == ln ) {
     5         kx         char *p = index( match, '=' ) + 1;
     5         kx         if( p != NULL ) pkgname = skip_spaces( p );
     5         kx       }
     5         kx       if( (match = strstr( ln, "pkgver" )) && match == ln ) {
     5         kx         char *p = index( match, '=' ) + 1;
     5         kx         if( p != NULL ) pkgver = skip_spaces( p );
     5         kx       }
     5         kx 
     5         kx       if( (match = strstr( ln, "group" )) && match == ln ) {
     5         kx         char *p = index( match, '=' ) + 1;
     5         kx         if( p != NULL ) group = skip_spaces( p );
     5         kx       }
     5         kx 
     5         kx       if( (match = strstr( ln, "arch" )) && match == ln ) {
     5         kx         char *p = index( match, '=' ) + 1;
     5         kx         if( p != NULL ) arch = skip_spaces( p );
     5         kx       }
     5         kx       if( (match = strstr( ln, "distroname" )) && match == ln ) {
     5         kx         char *p = index( match, '=' ) + 1;
     5         kx         if( p != NULL ) distroname = skip_spaces( p );
     5         kx       }
     5         kx       if( (match = strstr( ln, "distrover" )) && match == ln ) {
     5         kx         char *p = index( match, '=' ) + 1;
     5         kx         if( p != NULL ) distrover = skip_spaces( p );
     5         kx       }
     5         kx 
     5         kx       if( (match = strstr( ln, "short_description" )) && match == ln ) {
     5         kx         char *p = index( match, '=' ) + 1;
     5         kx         if( p != NULL )
     5         kx         {
     5         kx           char *b =  index( p, '"'),
     5         kx                *e = rindex( p, '"');
     5         kx           if( b && e && ( b != e ) )
     5         kx           {
     5         kx             p = ++b; *e = '\0';
     5         kx             short_description = xstrdup( (const char *)p );
     5         kx           }
     5         kx         }
     5         kx       }
     5         kx       if( (match = strstr( ln, "url" )) && match == ln ) {
     5         kx         char *p = index( match, '=' ) + 1;
     5         kx         if( p != NULL ) url = skip_spaces( p );
     5         kx       }
     5         kx       if( (match = strstr( ln, "license" )) && match == ln ) {
     5         kx         char *p = index( match, '=' ) + 1;
     5         kx         if( p != NULL ) license = skip_spaces( p );
     5         kx       }
     5         kx     }
     5         kx 
     5         kx     free( line );
     5         kx 
     5         kx     if( !pkgname || !pkgver || !arch || !distroname || !distrover )
     5         kx     {
     5         kx       FATAL_ERROR( "Invalid input .PKGINFO file" );
     5         kx     }
     5         kx 
     5         kx     if( !url )     { url     = xstrdup( DISTRO_URL );     }
     5         kx     if( !license ) { license = xstrdup( DISTRO_LICENSE ); }
     5         kx   }
     5         kx 
     5         kx   fclose( pkginfo );
     5         kx   free( buf );
     5         kx }
     5         kx 
     5         kx static void tune_destinations( void )
     5         kx {
     5         kx   if( mkgroupdir && group )
     5         kx   {
     5         kx     char  *buf = NULL;
     5         kx 
     5         kx     buf = (char *)malloc( (size_t)PATH_MAX );
     5         kx     if( !buf ) { FATAL_ERROR( "Cannot allocate memory" ); }
     5         kx     bzero( (void *)buf, PATH_MAX );
     5         kx 
     5         kx     (void)strcpy( buf, (const char *)destination );
     5         kx     (void)strcat( buf, "/" );
     5         kx     (void)strcat( buf, (const char *)group );
     5         kx     if( flavour )
     5         kx     {
     5         kx       (void)strcat( buf, "/" );
     5         kx       (void)strcat( buf, (const char *)flavour );
     5         kx     }
     5         kx 
     5         kx     if( _mkdir_p( buf, S_IRWXU | S_IRWXG | S_IRWXO ) != 0 )
     5         kx     {
     5         kx       FATAL_ERROR( "Cannot create target directory" );
     5         kx     }
     5         kx 
     5         kx     free( destination );
     5         kx     destination = xstrdup( (const char *)&buf[0] );
     5         kx     free( buf );
     5         kx   }
     5         kx 
     5         kx   /* here we can allocate memory for output filenames */
     5         kx }
     5         kx 
     5         kx /*********************************************
     5         kx   .RESTORELINKS functions:
     5         kx  */
     5         kx static void start_restorelinks_file( void )
     5         kx {
     5         kx   char *tmp = NULL;
     5         kx 
     5         kx   tmp = (char *)malloc( PATH_MAX );
     5         kx   if( !tmp ) { FATAL_ERROR( "Cannot allocate memory" ); }
     5         kx   bzero( (void *)tmp, PATH_MAX );
     5         kx 
     5         kx   (void)sprintf( (char *)&tmp[0], "%s/%s", tmpdir, ".RESTORELINKS" );
     5         kx 
     5         kx   rlinks = fopen( (const char *)&tmp[0], "w" );
     5         kx   if( !rlinks )
     5         kx   {
     5         kx     FATAL_ERROR( "Cannot create '.RESTORELINKS' file"  );
     5         kx   }
     5         kx   free( tmp );
     5         kx }
     5         kx 
     5         kx static void stop_restorelinks_file( void )
     5         kx {
     5         kx   struct stat sb;
     5         kx   char  *tmp = NULL, *cmd = NULL;
     5         kx   int    len = 0;
     5         kx 
     5         kx   fflush( rlinks ); fclose( rlinks );
     5         kx 
     5         kx   tmp = (char *)malloc( PATH_MAX );
     5         kx   if( !tmp ) { FATAL_ERROR( "Cannot allocate memory" ); }
     5         kx   bzero( (void *)tmp, PATH_MAX );
     5         kx 
     5         kx   (void)sprintf( (char *)&tmp[0], "%s/%s", tmpdir, ".RESTORELINKS" );
     5         kx 
     5         kx   if( stat( tmp, &sb ) == 0 )
     5         kx   {
     5         kx     if( S_ISREG(sb.st_mode) && sb.st_size != 0 )
     5         kx     {
     5         kx       pid_t p = (pid_t) -1;
     5         kx       int   rc;
     5         kx 
     5         kx       cmd = (char *)malloc( (size_t)PATH_MAX );
     5         kx       if( !cmd ) { FATAL_ERROR( "Cannot allocate memory" ); }
     5         kx       bzero( (void *)cmd, PATH_MAX );
     5         kx 
     5         kx       len = snprintf( &cmd[0], PATH_MAX, "cp %s %s/ > /dev/null 2>&1", tmp, srcdir );
     5         kx       if( len == 0 || len == PATH_MAX - 1 )
     5         kx       {
     5         kx         FATAL_ERROR( "Cannot create '.RESTORELINKS' file" );
     5         kx       }
     5         kx       p = sys_exec_command( cmd );
     5         kx       rc = sys_wait_command( p, (char *)NULL, PATH_MAX );
     5         kx       if( rc != 0 )
     5         kx       {
     5         kx         FATAL_ERROR( "Cannot create '.RESTORELINKS' file" );
     5         kx       }
     5         kx 
     5         kx       free( cmd );
     5         kx     }
     5         kx   }
     5         kx 
     5         kx   free( tmp );
     5         kx }
     5         kx 
     5         kx 
     5         kx static void save_link( const char *name_in, const char *name_to, const char *name_is )
     5         kx {
     5         kx   if( rlinks )
     5         kx   {
     5         kx     fprintf( rlinks, "( cd %s ; rm -rf %s )\n", name_in, name_is );
     5         kx     fprintf( rlinks, "( cd %s ; ln -sf %s %s )\n", name_in, name_to, name_is );
     5         kx   }
     5         kx }
     5         kx 
     5         kx /*
     5         kx   End of .RESTORELINKS functions.
     5         kx  *********************************************/
     5         kx 
     5         kx /*********************************************
     5         kx   File list functions:
     5         kx  */
     5         kx static void _print_filelist_entry( void *data, void *user_data )
     5         kx {
     5         kx   const char *path = (const char *)data;
     5         kx   FILE *output = (FILE *)user_data;
     5         kx 
     5         kx   if( !output ) output = stdout;
     5         kx 
     5         kx   fprintf( output, "%s\n", path );
     5         kx }
     5         kx 
     5         kx static void _free_filelist_entry( void *data, void *user_data )
     5         kx {
     5         kx   if( data ) { free( data ); }
     5         kx }
     5         kx 
     5         kx static int _compare_fnames( const void *a, const void *b )
     5         kx {
     5         kx   const char *s1 = (const char *)a;
     5         kx   const char *s2 = (const char *)b;
     5         kx 
     5         kx   return strcmp( s1, s2 );
     5         kx }
     5         kx 
     5         kx static void _push_file( const char *name )
     5         kx {
     5         kx   char *fname = (char *)name + strlen( srcdir ) + 1;
     5         kx   filelist = dlist_append( filelist, (void *)xstrdup( (const char *)fname ) );
     5         kx }
     5         kx 
     5         kx static void _push_dir( const char *name )
     5         kx {
     5         kx   char *buf   = NULL;
     5         kx   char *dname = (char *)name + strlen( srcdir ) + 1;
     5         kx 
     5         kx   buf = (char *)malloc( strlen( dname ) + 2 );
     5         kx   if( !buf ) { FATAL_ERROR( "Cannot allocate memory" ); }
     5         kx   (void)sprintf( &buf[0], "%s/", dname );
     5         kx 
     5         kx   filelist = dlist_append( filelist, (void *)xstrdup( (const char *)&buf[0] ) );
     5         kx   free( buf );
     5         kx }
     5         kx 
     5         kx static void _list_files( const char *dirpath )
     5         kx {
     5         kx   DIR    *dir;
     5         kx   char   *path;
     5         kx   size_t  len;
     5         kx 
     5         kx   struct stat    path_sb, entry_sb;
     5         kx   struct dirent *entry;
     5         kx 
     5         kx   if( stat( dirpath, &path_sb ) == -1 )
     5         kx   {
     5         kx     FATAL_ERROR( "%s: Cannot stat source directory", dirpath );
     5         kx   }
     5         kx 
     5         kx   if( S_ISDIR(path_sb.st_mode) == 0 )
     5         kx   {
     5         kx     FATAL_ERROR( "%s: Source path is not a directory", dirpath );
     5         kx   }
     5         kx 
     5         kx   if( (dir = opendir(dirpath) ) == NULL )
     5         kx   {
     5         kx     FATAL_ERROR( "Canot access '%s' directory: %s", dirpath, strerror( errno ) );
     5         kx   }
     5         kx 
     5         kx   len = strlen( dirpath );
     5         kx 
     5         kx   while( (entry = readdir( dir )) != NULL)
     5         kx   {
     5         kx     /* skip entries '.' and '..' */
     5         kx     if( ! strcmp( entry->d_name, "." ) || ! strcmp( entry->d_name, ".." ) ) continue;
     5         kx 
     5         kx     /* determinate a full name of an entry */
     5         kx     path = alloca( len + strlen( entry->d_name ) + 2 );
     5         kx 
     5         kx     strcpy( path, dirpath );
     5         kx     strcat( path, "/" );
     5         kx     strcat( path, entry->d_name );
     5         kx 
     5         kx     if( lstat( path, &entry_sb ) == 0 )
     5         kx     {
     5         kx       if( S_ISDIR(entry_sb.st_mode) )
     5         kx       {
     5         kx         _push_dir( (const char *)path );
     5         kx         pkgsize += (size_t)entry_sb.st_size;
     5         kx         _list_files( (const char *)path );
     5         kx       }
     5         kx       else
     5         kx       {
     5         kx         if( S_ISREG(entry_sb.st_mode) )
     5         kx         {
     5         kx           char *service = basename( path );
     5         kx 
     5         kx           /* skip service files: */
     5         kx           if( strcmp( service, ".DESCRIPTION"  ) &&
     5         kx               strcmp( service, ".FILELIST"     ) &&
     5         kx               strcmp( service, ".INSTALL"      ) &&
     5         kx               strcmp( service, ".PKGINFO"      ) &&
     5         kx               strcmp( service, ".REQUIRES"     ) &&
     5         kx               strcmp( service, ".RESTORELINKS" )   )
     5         kx           {
     5         kx             _push_file( (const char *)path );
     5         kx             pkgsize += (size_t)entry_sb.st_size;
     5         kx             ++nfiles;
     5         kx           }
     5         kx         }
     5         kx         if( S_ISBLK(entry_sb.st_mode)  ||
     5         kx             S_ISCHR(entry_sb.st_mode)  ||
     5         kx             S_ISFIFO(entry_sb.st_mode) ||
     5         kx             S_ISSOCK(entry_sb.st_mode)   )
     5         kx         {
     5         kx           _push_file( (const char *)path );
     5         kx           ++nfiles;
     5         kx         }
     5         kx         if( S_ISLNK(entry_sb.st_mode) )
     5         kx         {
     5         kx           if( linkadd )
     5         kx           {
     5         kx             char   *buf = NULL;
     5         kx             ssize_t len = 0;
     5         kx 
     5         kx             const char *in = (char *)dirpath + strlen( srcdir ) + 1;
     5         kx             const char *is = (char *)path + strlen( dirpath ) + 1;
     5         kx 
     5         kx             buf = (char *)malloc( PATH_MAX );
     5         kx             if( !buf ) { FATAL_ERROR( "Cannot allocate memory" ); }
     5         kx             bzero( (void *)buf, PATH_MAX );
     5         kx 
     5         kx             if( (len = readlink( (const char *)path, buf, PATH_MAX - 1 )) == -1 )
     5         kx             {
     5         kx               FATAL_ERROR( "%s: Cannot read link: %s", is, strerror( errno ) );
     5         kx             }
     5         kx             buf[len] = '\0';
     5         kx             save_link( in, (const char *)&buf[0], is );
     5         kx             free( buf );
     5         kx 
     5         kx             /* remove the link: */
     5         kx             (void)unlink( (const char *)path );
     5         kx           }
     5         kx           else
     5         kx           {
     5         kx             _push_file( (const char *)path );
     5         kx             ++nfiles;
     5         kx           }
     5         kx         }
     5         kx 
     5         kx       } /* End if( S_ISDIR(entry_sb.st_mode) ) */
     5         kx 
     5         kx     }
     5         kx     /* else { stat() returns error code; errno is set; and we have to continue the loop } */
     5         kx   }
     5         kx 
     5         kx   closedir( dir );
     5         kx }
     5         kx 
     5         kx static void create_file_list( void )
     5         kx {
     5         kx   start_restorelinks_file();
     5         kx   _list_files( (const char *)srcdir );
     5         kx   stop_restorelinks_file();
     5         kx 
     5         kx   if( filelist )
     5         kx   {
     5         kx     FILE *flist = NULL;
     5         kx     char *tmp   = NULL;
     5         kx 
     5         kx     tmp = (char *)malloc( PATH_MAX );
     5         kx     if( !tmp ) { FATAL_ERROR( "Cannot allocate memory" ); }
     5         kx     bzero( (void *)tmp, PATH_MAX );
     5         kx 
     5         kx     (void)sprintf( (char *)&tmp[0], "%s/%s", srcdir, ".FILELIST" );
     5         kx 
     5         kx     flist = fopen( (const char *)&tmp[0], "w" );
     5         kx     if( !flist )
     5         kx     {
     5         kx       FATAL_ERROR( "Cannot create '.FILELIST' file"  );
     5         kx     }
     5         kx 
     5         kx     /*********************************
     5         kx       save total number of files:
     5         kx      */
     5         kx     (void)sprintf( (char *)&tmp[0], "%d", nfiles );
     5         kx     total_files = xstrdup( (const char *)&tmp[0] );
     5         kx 
     5         kx     /*********************************
     5         kx       save uncompressed package size:
     5         kx      */
     5         kx     {
     5         kx       int    nd;
     5         kx       double sz = (double)pkgsize / (double)1024;
     5         kx 
     5         kx       if( sz > (double)1048576 )
     5         kx       {
     5         kx         sz = sz / (double)1048576;
     5         kx         /*
     5         kx           NOTE:
     5         kx           ----
     5         kx           Операция округления до одного знака после десятичной точки: sz = round(sz*10.0)/10.0;
     5         kx           здесь не нужна; можно обойтись вычислением количества цифр, выводимых на печать с помощью
     5         kx           формата '%.*g':
     5         kx 
     5         kx           Количество десятичных цифр, необходимое для предстваления целой части числа + 1(одна)
     5         kx           десятичная цифра после десятичной точки. Формат %.*g не будет выводить дробную часть
     5         kx           числа, если после округления, до одного знака после десятичной точки, дробная часть
     5         kx           равна нулю:
     5         kx          */
     5         kx         nd = (int)ceil(log10(floor(sz) + 1.0)) + 1;
     5         kx         (void)sprintf( (char *)&tmp[0], "%.*gG", nd, sz );
     5         kx       }
     5         kx       else if( sz > (double)1024 )
     5         kx       {
     5         kx         sz = sz / (double)1024;
     5         kx         nd = (int)ceil(log10(floor(sz) + 1.0)) + 1;
     5         kx         (void)sprintf( (char *)&tmp[0], "%.*gM", nd, sz );
     5         kx       }
     5         kx       else
     5         kx       {
     5         kx         nd = (int)ceil(log10(floor(sz) + 1.0)) + 1;
     5         kx         (void)sprintf( (char *)&tmp[0], "%.*gK", nd, sz );
     5         kx       }
     5         kx     }
     5         kx     uncompressed_size = xstrdup( (const char *)&tmp[0] );
     5         kx 
     5         kx     free( tmp );
     5         kx 
     5         kx     filelist = dlist_sort( filelist, _compare_fnames );
     5         kx     dlist_foreach( filelist, _print_filelist_entry, flist );
     5         kx 
     5         kx     fflush( flist );
     5         kx     fclose( flist );
     5         kx   }
     5         kx   else
     5         kx   {
     5         kx     FATAL_ERROR( "There are no files in the source package"  );
     5         kx   }
     5         kx }
     5         kx 
     5         kx static void free_file_list( void )
     5         kx {
     5         kx   if( filelist ) { dlist_free( filelist, _free_filelist_entry ); filelist = NULL; }
     5         kx }
     5         kx /*
     5         kx   End of file list functions.
     5         kx  *********************************************/
     5         kx 
     5         kx /*********************************************
     5         kx   Description functions.
     5         kx  */
     5         kx static void get_short_description( char *buf, const char *line )
     5         kx {
     5         kx   char *s, *p, *q;
     5         kx 
     5         kx   if( buf ) { buf[0] = '\0'; s = buf; }
     5         kx   if( !line || line[0] == '\0' ) return;
     5         kx 
     5         kx   p = index( line, '(' );
     5         kx   q = index( line, ')' );
     5         kx   if( p && q && q > p )
     5         kx   {
     5         kx     ++p;
     5         kx     while( *p && p < q )
     5         kx     {
     5         kx       *s = *p;
     5         kx       ++p; ++s;
     5         kx     }
     5         kx     *s = '\0';
     5         kx   }
     5         kx   else
     5         kx   {
     5         kx     /*
     5         kx       If short description declaration is incorrect at first line
     5         kx       of description; then we take whole first line of description:
     5         kx      */
     5         kx     p = index( line, ':' ); ++p;
     5         kx     while( (*p == ' ' || *p == '\t') && *p != '\0' ) { ++p; }
     5         kx     strcpy( buf, p );
     5         kx   }
     5         kx }
     5         kx 
     5         kx static char *read_short_description( FILE *fh )
     5         kx {
     5         kx   char *ret  = NULL;
     5         kx   char *buf  = NULL;
     5         kx 
     5         kx   char *ln   = NULL;
     5         kx   char *line = NULL;
     5         kx 
     5         kx   buf = (char *)malloc( (size_t)PATH_MAX );
     5         kx   if( !buf ) { FATAL_ERROR( "Cannot allocate memory" ); }
     5         kx   bzero( (void *)buf, PATH_MAX );
     5         kx 
     5         kx   line = (char *)malloc( (size_t)PATH_MAX );
     5         kx   if( !line ) { FATAL_ERROR( "Cannot allocate memory" ); }
     5         kx   bzero( (void *)line, PATH_MAX );
     5         kx 
     5         kx   /* Get short_description from PACKAGE DESCRIPTION */
     5         kx   ln = fgets( line, PATH_MAX, fh );
     5         kx   ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */
     5         kx 
     5         kx   get_short_description( buf, (const char *)line );
     5         kx   if( buf[0] != '\0' )
     5         kx   {
     5         kx     ret = xstrdup( (const char *)buf );
     5         kx   }
     5         kx   free( buf );
     5         kx 
     5         kx   return ret;
     5         kx }
     5         kx 
     5         kx static void create_description_file( void )
     5         kx {
     5         kx   struct stat sb;
     5         kx   char  *buf  = NULL, *tmp = NULL;
     5         kx   int    n = 0;
     5         kx 
     5         kx   char *ln   = NULL;
     5         kx   char *line = NULL;
     5         kx 
     5         kx   FILE *srcdesc = NULL;
     5         kx   FILE *tmpdesc = NULL;
     5         kx   FILE *outdesc = NULL;
     5         kx 
     5         kx   buf = (char *)malloc( PATH_MAX );
     5         kx   if( !buf ) { FATAL_ERROR( "Cannot allocate memory" ); }
     5         kx   bzero( (void *)buf, PATH_MAX );
     5         kx 
     5         kx   tmp = (char *)malloc( PATH_MAX );
     5         kx   if( !tmp ) { FATAL_ERROR( "Cannot allocate memory" ); }
     5         kx   bzero( (void *)tmp, PATH_MAX );
     5         kx 
     5         kx   line = (char *)malloc( (size_t)PATH_MAX );
     5         kx   if( !line ) { FATAL_ERROR( "Cannot allocate memory" ); }
     5         kx   bzero( (void *)line, PATH_MAX );
     5         kx 
     5         kx   (void)sprintf( (char *)&tmp[0], "%s/%s", srcdir, ".DESCRIPTION" );
     5         kx 
     5         kx   if( stat( tmp, &sb ) == 0 )
     5         kx   {
     5         kx     if( S_ISREG(sb.st_mode) && sb.st_size != 0 )
     5         kx     {
     5         kx       srcdesc = fopen( (const char *)&tmp[0], "r" );
     5         kx       if( !srcdesc )
     5         kx       {
     5         kx         FATAL_ERROR( "Cannot read source '.DESCRIPTION' file"  );
     5         kx       }
     5         kx       bzero( (void *)tmp, PATH_MAX );
     5         kx 
     5         kx       (void)sprintf( (char *)&tmp[0], "%s/%s", tmpdir, ".DESCRIPTION" );
     5         kx       tmpdesc = fopen( (const char *)&tmp[0], "w+" );
     5         kx       if( !tmpdesc )
     5         kx       {
     5         kx         FATAL_ERROR( "Cannot create output '.DESCRIPTION' file"  );
     5         kx       }
     5         kx       bzero( (void *)tmp, PATH_MAX );
     5         kx 
     5         kx       (void)sprintf( (char *)&tmp[0], "%s/%s-%s-%s-%s-%s.txt",
     5         kx                                        destination, pkgname, pkgver, arch, distroname, distrover );
     5         kx       outdesc = fopen( (const char *)&tmp[0], "w" );
     5         kx       if( !outdesc )
     5         kx       {
     5         kx         FATAL_ERROR( "Cannot create output '.DESCRIPTION' file"  );
     5         kx       }
     5         kx       bzero( (void *)tmp, PATH_MAX );
     5         kx 
     5         kx       (void)sprintf( (char *)&buf[0], "%s:", pkgname );
     5         kx 
     5         kx       fprintf( outdesc, "\n/* begin *\n\n" );
     5         kx 
     5         kx       while( (ln = fgets( line, PATH_MAX, srcdesc )) && n < DESCRIPTION_NUMBER_OF_LINES )
     5         kx       {
     5         kx         char *match = NULL;
     5         kx 
     5         kx         ln[strlen(ln) - 1] = '\0'; /* replace new-line symbol */
     5         kx 
     5         kx         if( (match = strstr( (const char *)ln, (const char *)buf )) && match == ln ) /* at start of line only */
     5         kx         {
     5         kx           int mlen   = strlen( match ), plen = strlen( buf );
     5         kx           int length = ( mlen > plen )  ? (mlen - plen - 1) : 0 ;
     5         kx 
     5         kx           if( length > DESCRIPTION_LENGTH_OF_LINE )
     5         kx           {
     5         kx             /* WARNING( "Package DESCRIPTION contains lines with length greater than %d characters", DESCRIPTION_LENGTH_OF_LINE ); */
     5         kx             match[plen + 1 + DESCRIPTION_LENGTH_OF_LINE] = '\0'; /* truncating description line  */
     5         kx             skip_eol_spaces( match );                            /* remove spaces at end-of-line */
     5         kx           }
     5         kx 
     5         kx           fprintf( tmpdesc, "%s\n", match );
     5         kx           match += plen + 1;
     5         kx           if( match[0] != '\0' ) { fprintf( outdesc, "   %s\n", match ); }
     5         kx           else                   { fprintf( outdesc, "\n" );             }
     5         kx           ++n;
     5         kx         }
     5         kx       }
     5         kx 
     5         kx       fprintf( outdesc, " * end */\n" );
     5         kx 
     5         kx       if( !short_description )
     5         kx       {
     5         kx         /* try to get short description from .DESCRIPTION file */
     5         kx         fseek( tmpdesc, 0, SEEK_SET );
     5         kx         short_description = read_short_description( tmpdesc );
     5         kx       }
     5         kx 
     5         kx       fflush( tmpdesc ); fclose( tmpdesc );
     5         kx       fflush( outdesc ); fclose( outdesc );
     5         kx       fclose( srcdesc );
     5         kx 
     5         kx       /* Copy tmpdesc file to the source package directory: */
     5         kx       {
     5         kx         char *cmd = NULL;
     5         kx 
     5         kx         bzero( (void *)tmp, PATH_MAX );
     5         kx         (void)sprintf( (char *)&tmp[0], "%s/%s", tmpdir, ".DESCRIPTION" );
     5         kx 
     5         kx         if( stat( tmp, &sb ) == 0 )
     5         kx         {
     5         kx           if( S_ISREG(sb.st_mode) && sb.st_size != 0 )
     5         kx           {
     5         kx             pid_t p = (pid_t) -1;
     5         kx             int   rc, len = 0;
     5         kx 
     5         kx             cmd = (char *)malloc( (size_t)PATH_MAX );
     5         kx             if( !cmd ) { FATAL_ERROR( "Cannot allocate memory" ); }
     5         kx             bzero( (void *)cmd, PATH_MAX );
     5         kx 
     5         kx             len = snprintf( &cmd[0], PATH_MAX, "cp %s %s/ > /dev/null 2>&1", tmp, srcdir );
     5         kx             if( len == 0 || len == PATH_MAX - 1 )
     5         kx             {
     5         kx               FATAL_ERROR( "Cannot create output '.DESCRIPTION' file" );
     5         kx             }
     5         kx             p = sys_exec_command( cmd );
     5         kx             rc = sys_wait_command( p, (char *)NULL, PATH_MAX );
     5         kx             if( rc != 0 )
     5         kx             {
     5         kx               FATAL_ERROR( "Cannot create output '.DESCRIPTION' file" );
     5         kx             }
     5         kx 
     5         kx             free( cmd );
     5         kx           }
     5         kx         }
     5         kx       } /* End of copy tmpdesc file. */
     5         kx     }
     5         kx   }
     5         kx 
     5         kx   free( line );
     5         kx   free( tmp );
     5         kx   free( buf );
     5         kx }
     5         kx /*
     5         kx   End of description functions.
     5         kx  *********************************************/
     5         kx 
     5         kx static void rewrite_pkginfo_file( void )
     5         kx {
     5         kx   FILE *info = NULL;
     5         kx   char *tmp  = NULL;
     5         kx 
     5         kx   tmp = (char *)malloc( PATH_MAX );
     5         kx   if( !tmp ) { FATAL_ERROR( "Cannot allocate memory" ); }
     5         kx   bzero( (void *)tmp, PATH_MAX );
     5         kx 
     5         kx   (void)sprintf( (char *)&tmp[0], "%s/%s", srcdir, ".PKGINFO" );
     5         kx 
     5         kx   info = fopen( (const char *)&tmp[0], "w" );
     5         kx   if( !info )
     5         kx   {
     5         kx     FATAL_ERROR( "Cannot create '.PKGINFO' file"  );
     5         kx   }
     5         kx 
     5         kx   if( pkgname )
     5         kx   {
     5         kx     (void)sprintf( (char *)&tmp[0], "pkgname=%s\n", pkgname );
     5         kx     fprintf( info, (const char *)&tmp[0] );
     5         kx   }
     5         kx   if( pkgver )
     5         kx   {
     5         kx     (void)sprintf( (char *)&tmp[0], "pkgver=%s\n", pkgver );
     5         kx     fprintf( info, (const char *)&tmp[0] );
     5         kx   }
     5         kx   if( arch )
     5         kx   {
     5         kx     (void)sprintf( (char *)&tmp[0], "arch=%s\n", arch );
     5         kx     fprintf( info, (const char *)&tmp[0] );
     5         kx   }
     5         kx   if( distroname )
     5         kx   {
     5         kx     (void)sprintf( (char *)&tmp[0], "distroname=%s\n", distroname );
     5         kx     fprintf( info, (const char *)&tmp[0] );
     5         kx   }
     5         kx   if( distrover )
     5         kx   {
     5         kx     (void)sprintf( (char *)&tmp[0], "distrover=%s\n", distrover );
     5         kx     fprintf( info, (const char *)&tmp[0] );
     5         kx   }
     5         kx   if( group )
     5         kx   {
     5         kx     (void)sprintf( (char *)&tmp[0], "group=%s\n", group );
     5         kx     fprintf( info, (const char *)&tmp[0] );
     5         kx   }
     5         kx   if( short_description )
     5         kx   {
     5         kx     (void)sprintf( (char *)&tmp[0], "short_description=\"%s\"\n", short_description );
     5         kx     fprintf( info, (const char *)&tmp[0] );
     5         kx   }
     5         kx   if( url )
     5         kx   {
     5         kx     (void)sprintf( (char *)&tmp[0], "url=%s\n", url );
     5         kx     fprintf( info, (const char *)&tmp[0] );
     5         kx   }
     5         kx   if( license )
     5         kx   {
     5         kx     (void)sprintf( (char *)&tmp[0], "license=%s\n", license );
     5         kx     fprintf( info, (const char *)&tmp[0] );
     5         kx   }
     5         kx   if( uncompressed_size )
     5         kx   {
     5         kx     (void)sprintf( (char *)&tmp[0], "uncompressed_size=%s\n", uncompressed_size );
     5         kx     fprintf( info, (const char *)&tmp[0] );
     5         kx   }
     5         kx   if( total_files )
     5         kx   {
     5         kx     (void)sprintf( (char *)&tmp[0], "total_files=%s\n", total_files );
     5         kx     fprintf( info, (const char *)&tmp[0] );
     5         kx   }
     5         kx 
     5         kx   free( tmp );
     5         kx 
     5         kx   fflush( info );
     5         kx   fclose( info );
     5         kx }
     5         kx 
     5         kx static const char *fill_compressor( char *buffer, char compressor )
     5         kx {
     5         kx   switch( compressor )
     5         kx   {
     5         kx     default:
     5         kx     case 'J':
     5         kx       (void)sprintf( buffer, "xz -9 --threads=%d -c", get_nprocs() );
     5         kx       break;
     5         kx     case 'j':
     5         kx       (void)sprintf( buffer, "bzip2 -9 -c" );
     5         kx       break;
     5         kx     case 'z':
     5         kx       (void)sprintf( buffer, "gzip -9 -c" );
     5         kx       break;
     5         kx   }
     5         kx   return (const char *)buffer;
     5         kx }
     5         kx 
     5         kx static void create_package( void )
     5         kx {
     5         kx   pid_t p = (pid_t) -1;
     5         kx   int   rc, len = 0;
     5         kx 
     5         kx   char *tmp = NULL, *cwd = NULL, *dst = NULL, *cmd = NULL;
     5         kx 
     5         kx #define tar_suffix ".tar"
     5         kx #define sha_suffix ".sha"
     5         kx #define asc_suffix ".asc"
     5         kx #define ACLS       "--acls"
     5         kx #define XATTRS     "--xattrs"
     5         kx #define HASHER     "sha256sum -b"
     5         kx 
     5         kx   char compressor[64];
     5         kx   (void)fill_compressor( (char *)&compressor[0], compress );
     5         kx 
     5         kx   tmp = (char *)malloc( PATH_MAX );
     5         kx   if( !tmp ) { FATAL_ERROR( "Cannot allocate memory" ); }
     5         kx   bzero( (void *)tmp, PATH_MAX );
     5         kx 
     5         kx   cwd = (char *)malloc( PATH_MAX );
     5         kx   if( !cwd ) { FATAL_ERROR( "Cannot allocate memory" ); }
     5         kx   bzero( (void *)cwd, PATH_MAX );
     5         kx 
     5         kx   dst = (char *)malloc( PATH_MAX );
     5         kx   if( !dst ) { FATAL_ERROR( "Cannot allocate memory" ); }
     5         kx   bzero( (void *)dst, PATH_MAX );
     5         kx 
     5         kx   cmd = (char *)malloc( PATH_MAX );
     5         kx   if( !cmd ) { FATAL_ERROR( "Cannot allocate memory" ); }
     5         kx   bzero( (void *)cmd, PATH_MAX );
     5         kx 
     5         kx   /* absolute current directory path: */
     5         kx   if( getcwd( cwd, (size_t)PATH_MAX ) == NULL )
     5         kx   {
     5         kx     FATAL_ERROR( "%s-%s-%s-%s-%s%s: Cannot create PACKAGE: %s",
     5         kx                   pkgname, pkgver, arch, distroname, distrover, txz_suffix, strerror( errno ) );
     5         kx   }
     5         kx   remove_trailing_slash( cwd );
     5         kx 
     5         kx   /****************************
     5         kx     absolute destination path:
     5         kx    */
     5         kx   (void)sprintf( (char *)&tmp[0], "%s", destination );
     5         kx   if( tmp[0] != '/' )
     5         kx   {
     5         kx     (void)sprintf( (char *)&dst[0], "%s/%s/%s", cwd, dirname( (char *)&tmp[0] ), basename( destination ) );
     5         kx   }
     5         kx   else
     5         kx   {
     5         kx     (void)sprintf( (char *)&dst[0], "%s", destination );
     5         kx   }
     5         kx 
     5         kx   /*****************************************
     5         kx     change CWD to source package directory:
     5         kx    */
     5         kx   if( chdir( (const char *)srcdir ) == -1 )
     5         kx   {
     5         kx     FATAL_ERROR( "Cannot change CWD to the package source directory" );
     5         kx   }
     5         kx 
     5         kx   /************************************
     5         kx     Set mode 0755 for .INSTALL script:
     5         kx    */
     5         kx   (void)sprintf( (char *)&cmd[0], "chmod 0755 ./.INSTALL" );
     5         kx   p = sys_exec_command( cmd );
     5         kx   rc = sys_wait_command( p, (char *)NULL, PATH_MAX );
     5         kx   if( rc != 0 )
     5         kx   {
     5         kx     FATAL_ERROR( "Cannot make .INSTAL script executable" );
     5         kx   }
     5         kx 
     5         kx   /**********************************
     5         kx     push package files into tarball:
     5         kx    */
     5         kx   len = snprintf( (char *)&cmd[0], PATH_MAX,
     5         kx     "find ./ | sed 's,^\\./,,' | "
     5         kx               "sed 's,\\.DESCRIPTION,,'  | "
     5         kx               "sed 's,\\.FILELIST,,'     | "
     5         kx               "sed 's,\\.INSTALL,,'      | "
     5         kx               "sed 's,\\.PKGINFO,,'      | "
     5         kx               "sed 's,\\.REQUIRES,,'     | "
     5         kx               "sed 's,\\.RESTORELINKS,,' | "
     5         kx               "tar --no-recursion %s %s -T - -cvf %s/%s-%s-%s-%s-%s%s",
     5         kx     ACLS, XATTRS, dst, pkgname, pkgver, arch, distroname, distrover, tar_suffix );
     5         kx   if( len == 0 || len == PATH_MAX - 1 )
     5         kx   {
     5         kx     FATAL_ERROR( "Cannot push package files into tarball" );
     5         kx   }
     5         kx   p = sys_exec_command( cmd );
     5         kx   rc = sys_wait_command( p, (char *)NULL, PATH_MAX );
     5         kx   if( rc != 0 )
     5         kx   {
     5         kx     FATAL_ERROR( "Cannot push package files into tarball" );
     5         kx   }
     5         kx 
     5         kx   /**********************************
     5         kx     push service files into tarball:
     5         kx    */
     5         kx   len = snprintf( (char *)&cmd[0], PATH_MAX,
     5         kx     "find ./ -type f \\( -name '.DESCRIPTION' -o "
     5         kx                         "-name '.FILELIST'    -o "
     5         kx                         "-name '.INSTALL'     -o "
     5         kx                         "-name '.PKGINFO'     -o "
     5         kx                         "-name '.REQUIRES'    -o "
     5         kx                         "-name '.RESTORELINKS' \\) | "
     5         kx               "sed 's,^\\./,,' | "
     5         kx               "tar --no-recursion %s %s -T - --append -f %s/%s-%s-%s-%s-%s%s",
     5         kx     ACLS, XATTRS, dst, pkgname, pkgver, arch, distroname, distrover, tar_suffix );
     5         kx   if( len == 0 || len == PATH_MAX - 1 )
     5         kx   {
     5         kx     FATAL_ERROR( "Cannot push service files into tarball" );
     5         kx   }
     5         kx   p = sys_exec_command( cmd );
     5         kx   rc = sys_wait_command( p, (char *)NULL, PATH_MAX );
     5         kx   if( rc != 0 )
     5         kx   {
     5         kx     FATAL_ERROR( "Cannot push service files into tarball" );
     5         kx   }
     5         kx 
     5         kx   /**********************************
     5         kx     push service files into tarball:
     5         kx    */
     5         kx   len = snprintf( (char *)&cmd[0], PATH_MAX,
     5         kx     "cat %s/%s-%s-%s-%s-%s%s | %s > %s/%s-%s-%s-%s-%s%s",
     5         kx          dst, pkgname, pkgver, arch, distroname, distrover, tar_suffix,
     5         kx          compressor, dst, pkgname, pkgver, arch, distroname, distrover, txz_suffix );
     5         kx   if( len == 0 || len == PATH_MAX - 1 )
     5         kx   {
     5         kx     FATAL_ERROR( "Cannot compress tarball" );
     5         kx   }
     5         kx   p = sys_exec_command( cmd );
     5         kx   rc = sys_wait_command( p, (char *)NULL, PATH_MAX );
     5         kx   if( rc != 0 )
     5         kx   {
     5         kx     FATAL_ERROR( "Cannot compress tarball" );
     5         kx   }
     5         kx 
     5         kx   /******************************
     5         kx     remove uncompressed tarball:
     5         kx    */
     5         kx   len = snprintf( (char *)&cmd[0], PATH_MAX,
     5         kx     "rm -f %s/%s-%s-%s-%s-%s%s",
     5         kx          dst, pkgname, pkgver, arch, distroname, distrover, tar_suffix );
     5         kx   if( len == 0 || len == PATH_MAX - 1 )
     5         kx   {
     5         kx     FATAL_ERROR( "Cannot remove umcompressed tarball" );
     5         kx   }
     5         kx   p = sys_exec_command( cmd );
     5         kx   rc = sys_wait_command( p, (char *)NULL, PATH_MAX );
     5         kx   if( rc != 0 )
     5         kx   {
     5         kx     FATAL_ERROR( "Cannot remove umcompressed tarball" );
     5         kx   }
     5         kx 
     5         kx   /***********************************
     5         kx     NOTE:
     5         kx       To check SHA sum we can make use 'shasum' utility:
     5         kx       $ ( cd destination ; shasum --check filename.sha )
     5         kx       without explicitly indicated algorithm [-a 256].
     5         kx 
     5         kx     generate SHA-256 sum of tarball:
     5         kx    */
     5         kx   len = snprintf( (char *)&cmd[0], PATH_MAX,
     5         kx     "%s %s/%s-%s-%s-%s-%s%s | sed 's,%s/,,' > %s/%s-%s-%s-%s-%s%s", HASHER,
     5         kx      dst, pkgname, pkgver, arch, distroname, distrover, txz_suffix,
     5         kx       dst, dst, pkgname, pkgver, arch, distroname, distrover, sha_suffix );
     5         kx   if( len == 0 || len == PATH_MAX - 1 )
     5         kx   {
     5         kx     FATAL_ERROR( "Cannot generate SHA-256 sum of package tarball" );
     5         kx   }
     5         kx   p = sys_exec_command( cmd );
     5         kx   rc = sys_wait_command( p, (char *)NULL, PATH_MAX );
     5         kx   if( rc != 0 )
     5         kx   {
     5         kx     FATAL_ERROR( "Cannot generate SHA-256 sum of package tarball" );
     5         kx   }
     5         kx 
     5         kx #if defined( HAVE_GPG2 )
     5         kx   /*******************************
     5         kx     generate GPG ascii-signature:
     5         kx    */
     5         kx   if( !gnupghome )
     5         kx   {
     5         kx     struct stat st;
     5         kx     char  *home = NULL;
     5         kx 
     5         kx     bzero( (void *)&st, sizeof( struct stat ) );
     5         kx 
     5         kx     home = getenv( "GNUPGHOME" );
     5         kx     if( home )
     5         kx     {
     5         kx       (void)sprintf( tmp, "%s", home );
     5         kx       if( (stat( (const char *)&tmp[0], &st ) == 0) && S_ISDIR(st.st_mode) )
     5         kx       {
     5         kx         gnupghome = xstrdup( (const char *)&tmp[0] );
     5         kx       }
     5         kx     }
     5         kx     else
     5         kx     {
     5         kx       home = getenv( "HOME" );
     5         kx       if( home )
     5         kx       {
     5         kx         (void)sprintf( tmp, "%s/.gnupg", home );
     5         kx         if( (stat( (const char *)&tmp[0], &st ) == 0) && S_ISDIR(st.st_mode) )
     5         kx         {
     5         kx           gnupghome = xstrdup( (const char *)&tmp[0] );
     5         kx         }
     5         kx       }
     5         kx     }
     5         kx     /* Check GNUPGHOME directory: */
     5         kx     while( gnupghome )
     5         kx     {
     5         kx       (void)sprintf( tmp, "%s/%s", gnupghome, "private-keys-v1.d" );
     5         kx       if( (stat( (const char *)&tmp[0], &st ) == -1) || !S_ISDIR(st.st_mode) )
     5         kx       {
     5         kx         free( gnupghome ); gnupghome = NULL;
     5         kx         break;
     5         kx       }
     5         kx       (void)sprintf( tmp, "%s/pubring.kbx", gnupghome );
     5         kx       if( (stat( (const char *)&tmp[0], &st ) == -1) || !S_ISREG(st.st_mode) )
     5         kx       {
     5         kx         free( gnupghome ); gnupghome = NULL;
     5         kx         break;
     5         kx       }
     5         kx       (void)sprintf( tmp, "%s/trustdb.gpg", gnupghome );
     5         kx       if( (stat( (const char *)&tmp[0], &st ) == -1) || !S_ISREG(st.st_mode) )
     5         kx       {
     5         kx         free( gnupghome ); gnupghome = NULL;
     5         kx         break;
     5         kx       }
     5         kx       break;
     5         kx     }
     5         kx   }
     5         kx   if( gnupghome && passphrase && key_id )
     5         kx   {
     5         kx     (void)sprintf( tmp, "%s/%s", tmpdir, ".gnupg" );
     5         kx     if( _mkdir_p( tmp, S_IRWXU | S_IRWXG | S_IRWXO ) == -1 )
     5         kx     {
     5         kx       FATAL_ERROR( "Cannot create temporary GnuPG home directory" );
     5         kx     }
     5         kx 
     5         kx     len = snprintf( (char *)&cmd[0], PATH_MAX,
     5         kx       "chmod 700 %s ;"
     5         kx       " cp %s/trustdb.gpg %s/ ;"
     5         kx       " cp %s/pubring.kbx %s/ ;"
     5         kx       " cp -r %s/private-keys-v1.d %s/",
     5         kx       tmp,
     5         kx       gnupghome, tmp,
     5         kx       gnupghome, tmp,
     5         kx       gnupghome, tmp );
     5         kx     if( len == 0 || len == PATH_MAX - 1 )
     5         kx     {
     5         kx       FATAL_ERROR( "Cannot prepare temporary GnuPG home for signing" );
     5         kx     }
     5         kx     p = sys_exec_command( cmd );
     5         kx     rc = sys_wait_command( p, (char *)NULL, PATH_MAX );
     5         kx     if( rc != 0 )
     5         kx     {
     5         kx       ERROR( "Cannot prepare temporary GnuPG home for signing" );
     5         kx     }
     5         kx 
     5         kx     len = snprintf( (char *)&cmd[0], PATH_MAX,
     5         kx       "cat %s | GNUPGHOME=%s gpg2 -u %s --batch --passphrase-fd=0"
     5         kx                " --pinentry-mode=loopback"
     5         kx                " --armor --yes --emit-version"
     5         kx                " --comment %s-%s"
     5         kx                " -o %s/%s-%s-%s-%s-%s%s"
     5         kx                " --detach-sign %s/%s-%s-%s-%s-%s%s 2>/dev/null 1>/dev/null",
     5         kx       passphrase, tmp, key_id,
     5         kx       pkgname, pkgver,
     5         kx       dst, pkgname, pkgver, arch, distroname, distrover, asc_suffix,
     5         kx       dst, pkgname, pkgver, arch, distroname, distrover, txz_suffix );
     5         kx     if( len == 0 || len == PATH_MAX - 1 )
     5         kx     {
     5         kx       FATAL_ERROR( "Cannot generate GPG signature of package tarball" );
     5         kx     }
     5         kx     p = sys_exec_command( cmd );
     5         kx     rc = sys_wait_command( p, (char *)NULL, PATH_MAX );
     5         kx     if( rc != 0 )
     5         kx     {
     5         kx       ERROR( "Cannot generate GPG signature of package tarball" );
     5         kx     }
     5         kx   }
     5         kx #endif
     5         kx 
     5         kx   /******************
     5         kx     change CWD back:
     5         kx    */
     5         kx   if( chdir( (const char *)&cwd[0] ) == -1 )
     5         kx   {
     5         kx     FATAL_ERROR( "Cannot change CWD back" );
     5         kx   }
     5         kx 
     5         kx   fprintf( stdout, "\n%s package %s/%s-%s-%s-%s-%s%s has been created.\n\n",
     5         kx                       DISTRO_CAPTION,
     5         kx                         destination, pkgname, pkgver, arch, distroname, distrover, txz_suffix );
     5         kx 
     5         kx   free( cmd );
     5         kx   free( dst );
     5         kx   free( cwd );
     5         kx   free( tmp );
     5         kx }
     5         kx 
     5         kx 
     5         kx 
     5         kx /*********************************************
     5         kx   Get directory where this program is placed:
     5         kx  */
     5         kx char *get_selfdir( void )
     5         kx {
     5         kx   char    *buf = NULL;
     5         kx   ssize_t  len;
     5         kx 
     5         kx   buf = (char *)malloc( (size_t)PATH_MAX );
     5         kx   if( !buf )
     5         kx   {
     5         kx     FATAL_ERROR( "Cannot allocate memory" );
     5         kx   }
     5         kx 
     5         kx   bzero( (void *)buf, PATH_MAX );
     5         kx   len = readlink( "/proc/self/exe", buf, (size_t)PATH_MAX );
     5         kx   if( len > 0 && len < PATH_MAX )
     5         kx   {
     5         kx     char *p = xstrdup( (const char *)dirname( buf ) );
     5         kx     free( buf );
     5         kx     return p;
     5         kx   }
     5         kx   FATAL_ERROR( "Cannot determine self directory. Please mount /proc filesystem" );
     5         kx }
     5         kx 
     5         kx void set_stack_size( void )
     5         kx {
     5         kx   const rlim_t   stack_size = 16 * 1024 * 1024; /* min stack size = 16 MB */
     5         kx   struct rlimit  rl;
     5         kx   int ret;
     5         kx 
     5         kx   ret = getrlimit( RLIMIT_STACK, &rl );
     5         kx   if( ret == 0 )
     5         kx   {
     5         kx     if( rl.rlim_cur < stack_size )
     5         kx     {
     5         kx       rl.rlim_cur = stack_size;
     5         kx       ret = setrlimit( RLIMIT_STACK, &rl );
     5         kx       if( ret != 0 )
     5         kx       {
     5         kx         fprintf(stderr, "setrlimit returned result = %d\n", ret);
     5         kx         FATAL_ERROR( "Cannot set stack size" );
     5         kx       }
     5         kx     }
     5         kx   }
     5         kx }
     5         kx 
     5         kx 
     5         kx int main( int argc, char *argv[] )
     5         kx {
     5         kx   gid_t  gid;
     5         kx 
     5         kx   set_signal_handlers();
     5         kx 
     5         kx   gid = getgid();
     5         kx   setgroups( 1, &gid );
     5         kx 
     5         kx   fatal_error_hook = fatal_error_actions;
     5         kx 
     5         kx   selfdir = get_selfdir();
     5         kx 
     5         kx   errlog = stderr;
     5         kx 
     5         kx   program = basename( argv[0] );
     5         kx   get_args( argc, argv );
     5         kx 
     5         kx   /* set_stack_size(); */
     5         kx 
     5         kx   tmpdir = _mk_tmpdir();
     5         kx   if( !tmpdir )
     5         kx   {
     5         kx     FATAL_ERROR( "Cannot create temporary directory" );
     5         kx   }
     5         kx 
     5         kx   read_pkginfo();
     5         kx   tune_destinations();
     5         kx   create_file_list();
     5         kx   create_description_file();
     5         kx   /*
     5         kx     NOTE:
     5         kx       rewrite_pkginfo_file() should be called after create_description_file()
     5         kx       because if there is no short description in the source .PKGINFO file
     5         kx       then we can try to get the short description from the first line of
     5         kx       .DESCRIPTION file (between opening and closing round brackets).
     5         kx    */
     5         kx   rewrite_pkginfo_file();
     5         kx   create_package();
     5         kx 
     5         kx 
     5         kx   if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); }
     5         kx   free_resources();
     5         kx 
     5         kx   exit( exit_status );
     5         kx }