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 <sys/file.h> /* flock(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 <strings.h>  /* index(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/wait.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 <config.h>
     5         kx 
     5         kx #include <msglog.h>
     5         kx #include <wrapper.h>
     5         kx #include <system.h>
     5         kx 
     5         kx #include <cmpvers.h>
     5         kx #include <dlist.h>
     5         kx 
     5         kx #if defined( HAVE_DIALOG )
     5         kx #include <dialog-ui.h>
     5         kx #endif
     5         kx 
     5         kx #define PROGRAM_NAME "update-package"
     5         kx 
     5         kx #include <defs.h>
     5         kx 
     5         kx 
     5         kx char *program = PROGRAM_NAME;
     5         kx char *root = NULL, *pkgs_path = NULL, *rempkgs_path = NULL, *remlog_fname = NULL,
     5         kx      *pkg_fname = NULL, *asc_fname = NULL, *pkglog_fname = NULL, *pkglist_fname = NULL,
     5         kx      *tmpdir = NULL, *rtmpdir = NULL, *curdir = NULL, *log_fname = NULL;
     5         kx 
     5         kx int   ask = 0, rqck = 0, gpgck = 0, reinstall = 0, ignore_chrefs_errors = 0;
     5         kx char *description = NULL;
     5         kx 
     5         kx int   exit_status = EXIT_SUCCESS; /* errors counter */
     5         kx char *selfdir     = NULL;
     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               *compressed_size = NULL,
     5         kx                   *total_files = NULL;
     5         kx 
     5         kx static char *installed_version = NULL;
     5         kx static char *installed_group   = NULL;
     5         kx 
     5         kx enum _update_mode {
     5         kx   CONSOLE = 0,
     5         kx   INFODIALOG,
     5         kx   MENUDIALOG
     5         kx } update_mode = CONSOLE;
     5         kx 
     5         kx enum _priority {
     5         kx   REQUIRED = 0, /* synonims: REQUIRED    | required    | REQ | req */
     5         kx   RECOMMENDED,  /* synonims: RECOMMENDED | recommended | REC | rec */
     5         kx   OPTIONAL,     /* synonims: OPTIONAL    | optional    | OPT | opt */
     5         kx   SKIP          /* synonims: SKIP        | skip        | SKP | skp */
     5         kx } priority = REQUIRED;
     5         kx 
     5         kx enum _procedure
     5         kx {
     5         kx   INSTALL = 0, /* 'install' */
     5         kx   UPDATE       /* 'update'  */
     5         kx } procedure = UPDATE;
     5         kx 
     5         kx enum _input_type {
     5         kx   IFMT_PKG = 0,
     5         kx   IFMT_LOG,
     5         kx 
     5         kx   IFMT_UNKNOWN
     5         kx } input_format = IFMT_PKG;
     5         kx 
     5         kx char  uncompress = '\0';
     5         kx 
     5         kx static struct dlist *rdirs  = NULL;
     5         kx static struct dlist *rfiles = NULL;
     5         kx static struct dlist *rlinks = NULL;
     5         kx 
     5         kx static struct dlist *dirs  = NULL;
     5         kx static struct dlist *files = NULL;
     5         kx static struct dlist *links = NULL;
     5         kx 
     5         kx static void free_list( struct dlist *list );
     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( description )       { free( description );       } 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( compressed_size )   { free( compressed_size );   } compressed_size = NULL;    \
     5         kx   if( total_files )       { free( total_files );       } total_files = NULL;        \
     5         kx   if( installed_version ) { free( installed_version ); } installed_version = NULL;  \
     5         kx   if( installed_group )   { free( installed_group );   } installed_group = NULL
     5         kx 
     5         kx void free_resources()
     5         kx {
     5         kx   if( root )          { free( root );          root          = NULL; }
     5         kx   if( pkgs_path )     { free( pkgs_path );     pkgs_path     = NULL; }
     5         kx   if( rempkgs_path )  { free( rempkgs_path );  rempkgs_path  = NULL; }
     5         kx   if( pkg_fname )     { free( pkg_fname );     pkg_fname     = NULL; }
     5         kx   if( asc_fname )     { free( asc_fname );     asc_fname     = NULL; }
     5         kx   if( pkglog_fname )  { free( pkglog_fname );  pkglog_fname  = NULL; }
     5         kx   if( remlog_fname )  { free( remlog_fname );  remlog_fname  = NULL; }
     5         kx 
     5         kx   if( pkglist_fname ) { free( pkglist_fname ); pkglist_fname = NULL; }
     5         kx 
     5         kx   if( rdirs )         { free_list( rdirs );    rdirs         = NULL; }
     5         kx   if( rfiles )        { free_list( rfiles );   rfiles        = NULL; }
     5         kx   if( rlinks )        { free_list( rlinks );   rlinks        = NULL; }
     5         kx 
     5         kx   if( dirs )          { free_list( dirs );     dirs          = NULL; }
     5         kx   if( files )         { free_list( files );    files         = NULL; }
     5         kx   if( links )         { free_list( links );    links         = NULL; }
     5         kx 
     5         kx   if( rtmpdir )       { free( rtmpdir );       rtmpdir       = NULL; }
     5         kx   if( curdir )        { free( curdir );        curdir        = NULL; }
     5         kx   if( log_fname )     { free( log_fname );     log_fname     = NULL; }
     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] <package>\n", program );
     5         kx   fprintf( stdout, "\n" );
     5         kx   fprintf( stdout, "Update package.\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, "  -a,--always-ask               Used with menudialog mode: always ask\n" );
     5         kx   fprintf( stdout, "                                if a package should be updated regardless\n" );
     5         kx   fprintf( stdout, "                                of what the package priority is. Without\n" );
     5         kx   fprintf( stdout, "                                this option, if the priority is equal to\n" );
     5         kx   fprintf( stdout, "                                REQUIRED, the package is updateded without\n" );
     5         kx   fprintf( stdout, "                                asking for confirmation the update.\n" );
     5         kx   fprintf( stdout, "  -c,--check-requires           Check package requires before update.\n" );
     5         kx #if defined( HAVE_GPG2 )
     5         kx   fprintf( stdout, "  -g,--gpg-verify               Verify GPG2 signature. The signature must be\n" );
     5         kx   fprintf( stdout, "                                saved in a file whose name is the same as the\n" );
     5         kx   fprintf( stdout, "                                package file name, but with the extension '.asc'\n" );
     5         kx   fprintf( stdout, "                                and located in the same directory as the package.\n" );
     5         kx #endif
     5         kx   fprintf( stdout, "  --ignore-chrefs-errors        Ignore change references errors (code: 48).\n" );
     5         kx #if defined( HAVE_DIALOG )
     5         kx   fprintf( stdout, "  -i,--info-dialog              Show package description during update\n" );
     5         kx   fprintf( stdout, "                                process using ncurses dialog.\n" );
     5         kx   fprintf( stdout, "  -m,--menu-dialog              Ask for confirmation the update,\n" );
     5         kx   fprintf( stdout, "                                unless the priority is REQUIRED.\n" );
     5         kx #endif
     5         kx   fprintf( stdout, "  -l,--pkglist=<FILENAME>       Specify a different package list file\n" );
     5         kx   fprintf( stdout, "                                to use for read package priority and type\n" );
     5         kx   fprintf( stdout, "                                of install procedure. By default used the\n" );
     5         kx   fprintf( stdout, "                                '.pkglist' file found in the directory\n" );
     5         kx   fprintf( stdout, "                                where source package is placed.\n" );
     5         kx   fprintf( stdout, "  -p,--priority=<required|recommended|optional|skip>\n" );
     5         kx   fprintf( stdout, "                                Provides a priority of package instead of\n" );
     5         kx   fprintf( stdout, "                                the priority defined in the .pkglist file.\n" );
     5         kx   fprintf( stdout, "  --reinstall                   Reinstall even if the package is already\n" );
     5         kx   fprintf( stdout, "                                installed correctly.\n" );
     5         kx   fprintf( stdout, "                                Without this option, the already installed package\n" );
     5         kx   fprintf( stdout, "                                will not be updated. The update procedure will be\n" );
     5         kx   fprintf( stdout, "                                stopped with success return code.\n" );
     5         kx   fprintf( stdout, "  -r,--root=<DIR>               Target rootfs path.\n" );
     5         kx   fprintf( stdout, "\n" );
     5         kx   fprintf( stdout, "Parameter:\n" );
     5         kx   fprintf( stdout, "  <package>                     The PACKAGE tarball.\n" );
     5         kx   fprintf( stdout, "\n" );
     5         kx   fprintf( stdout, "Return codes:\n" );
     5         kx   fprintf( stdout, "  ------+-------------------------------------------------------\n" );
     5         kx   fprintf( stdout, "   code | status\n"  );
     5         kx   fprintf( stdout, "  ------+-------------------------------------------------------\n" );
     5         kx   fprintf( stdout, "     30 | package is not installed\n" );
     5         kx   fprintf( stdout, "    ----+----\n" );
     5         kx   fprintf( stdout, "     41 | update is aborted due to priority=SKIP\n" );
     5         kx   fprintf( stdout, "     42 | .pkglist appointed the 'install' procedure instead\n" );
     5         kx   fprintf( stdout, "     43 | pre-update script returned error status\n" );
     5         kx   fprintf( stdout, "     44 | uncompress process returned error status\n" );
     5         kx   fprintf( stdout, "     45 | restore-links script returned error status\n" );
     5         kx   fprintf( stdout, "     46 | post-update script returned error status\n" );
     5         kx   fprintf( stdout, "     47 | PKGLOG cannot be stored in the Setup Database\n" );
     5         kx   fprintf( stdout, "     48 | references cannot be updated in Setup Database\n" );
     5         kx   fprintf( stdout, "     49 | requires cannot be updated in Setup Database\n" );
     5         kx #if defined( HAVE_GPG2 )
     5         kx   fprintf( stdout, "    ----+----\n" );
     5         kx   fprintf( stdout, "     51 | signature verification returned error status\n" );
     5         kx #endif
     5         kx   fprintf( stdout, "  ------+-------------------------------------------------------\n"  );
     5         kx   fprintf( stdout, "\n" );
     5         kx   fprintf( stdout, "Upon successful completion zero is returned. Other non-zero return\n" );
     5         kx   fprintf( stdout, "codes imply incorrect completion of the update procedure.\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 void bind_asc_extention( char *name )
     5         kx {
     5         kx   char *p = NULL, *q = NULL;
     5         kx 
     5         kx   if( (p = rindex( name, '.' )) && (strlen(p) < 5) )
     5         kx   {
     5         kx     if( !strncmp( p, ".gz",  3 ) ||
     5         kx         !strncmp( p, ".bz2", 4 ) ||
     5         kx         !strncmp( p, ".xz",  3 )   )
     5         kx     {
     5         kx       *p = '\0';
     5         kx       q = rindex( name, '.' );
     5         kx       if( q && (strlen(q) < 5) && !strncmp( q, ".tar", 4 ) )
     5         kx       {
     5         kx         *q = '\0';
     5         kx       }
     5         kx     }
     5         kx     else if( !strncmp( p, ".tar", 4 ) ||
     5         kx              !strncmp( p, ".tbz", 4 ) ||
     5         kx              !strncmp( p, ".tgz", 4 ) ||
     5         kx              !strncmp( p, ".txz", 4 )   )
     5         kx     {
     5         kx       *p = '\0';
     5         kx     }
     5         kx   }
     5         kx 
     5         kx   (void)strcat( name, ".asc" );
     5         kx }
     5         kx 
     5         kx ////////////////////////////////////////////////////
     5         kx //static char *strmode( enum _update_mode mode )
     5         kx //{
     5         kx //  char *p = NULL;
     5         kx //
     5         kx //  switch( mode )
     5         kx //  {
     5         kx //    case CONSOLE:
     5         kx //      p = "CONSOLE";
     5         kx //      break;
     5         kx //    case INFODIALOG:
     5         kx //      p = "INFODIALOG";
     5         kx //      break;
     5         kx //    case MENUDIALOG:
     5         kx //      p = "MENUDIALOG";
     5         kx //      break;
     5         kx //  }
     5         kx //  return p;
     5         kx //}
     5         kx ////////////////////////////////////////////////////
     5         kx 
     5         kx static char *strprio( enum _priority priority, int short_name )
     5         kx {
     5         kx   char *p = NULL;
     5         kx 
     5         kx   switch( priority )
     5         kx   {
     5         kx     case REQUIRED:
     5         kx       p = ( short_name ) ? "REQ" : "required";
     5         kx       break;
     5         kx     case RECOMMENDED:
     5         kx       p = ( short_name ) ? "REC" : "recommended";
     5         kx       break;
     5         kx     case OPTIONAL:
     5         kx       p = ( short_name ) ? "OPT" : "optional";
     5         kx       break;
     5         kx     case SKIP:
     5         kx       p = ( short_name ) ? "SKP" : "skip";
     5         kx       break;
     5         kx   }
     5         kx   return p;
     5         kx }
     5         kx 
     5         kx static char *strproc( enum _procedure procedure )
     5         kx {
     5         kx   char *p = NULL;
     5         kx 
     5         kx   switch( procedure )
     5         kx   {
     5         kx     case INSTALL:
     5         kx       p = "install";
     5         kx       break;
     5         kx     case UPDATE:
     5         kx       p = "update";
     5         kx       break;
     5         kx   }
     5         kx   return p;
     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 static enum _input_type check_input_file( char *uncompress, const char *fname )
     5         kx {
     5         kx   struct stat st;
     5         kx   size_t pkglog_size = 0;
     5         kx   unsigned char buf[8];
     5         kx   int rc, fd;
     5         kx 
     5         kx   /* SIGNATURES: https://www.garykessler.net/library/file_sigs.html */
     5         kx 
     5         kx   if( uncompress )
     5         kx   {
     5         kx     *uncompress = '\0';
     5         kx   }
     5         kx 
     5         kx   if( stat( fname, &st ) == -1 )
     5         kx   {
     5         kx     FATAL_ERROR( "Cannot access %s file: %s", basename( (char *)fname ), strerror( errno ) );
     5         kx   }
     5         kx 
     5         kx   pkglog_size = st.st_size;
     5         kx 
     5         kx   if( (fd = open( fname, O_RDONLY )) == -1 )
     5         kx   {
     5         kx     FATAL_ERROR( "Cannot open %s file: %s", basename( (char *)fname ), strerror( errno ) );
     5         kx   }
     5         kx 
     5         kx   rc = (int)read( fd, (void *)&buf[0], 7 );
     5         kx   if( rc != 7 )
     5         kx   {
     5         kx     close( fd ); return IFMT_UNKNOWN;
     5         kx   }
     5         kx   buf[7] = '\0';
     5         kx 
     5         kx   /* TEXT */
     5         kx   if( !strncmp( (const char *)&buf[0], "PACKAGE", 7 ) )
     5         kx   {
     5         kx     close( fd ); return IFMT_LOG;
     5         kx   }
     5         kx 
     5         kx   /* GZ */
     5         kx   if( buf[0] == 0x1F && buf[1] == 0x8B && buf[2] == 0x08 )
     5         kx   {
     5         kx     if( uncompress ) { *uncompress = 'x'; }
     5         kx     close( fd ); return IFMT_PKG;
     5         kx   }
     5         kx 
     5         kx   /* BZ2 */
     5         kx   if( buf[0] == 0x42 && buf[1] == 0x5A && buf[2] == 0x68 )
     5         kx   {
     5         kx     if( uncompress ) { *uncompress = 'j'; }
     5         kx     close( fd ); return IFMT_PKG;
     5         kx   }
     5         kx 
     5         kx   /* XZ */
     5         kx   if( buf[0] == 0xFD && buf[1] == 0x37 && buf[2] == 0x7A &&
     5         kx       buf[3] == 0x58 && buf[4] == 0x5A && buf[5] == 0x00   )
     5         kx   {
     5         kx     if( uncompress ) { *uncompress = 'J'; }
     5         kx     close( fd ); return IFMT_PKG;
     5         kx   }
     5         kx 
     5         kx   if( pkglog_size > 262 )
     5         kx   {
     5         kx     if( lseek( fd, 257, SEEK_SET ) == -1 )
     5         kx     {
     5         kx       FATAL_ERROR( "Cannot check signature of %s file: %s", basename( (char *)fname ), strerror( errno ) );
     5         kx     }
     5         kx     rc = (int)read( fd, &buf[0], 5 );
     5         kx     if( rc != 5 )
     5         kx     {
     5         kx       FATAL_ERROR( "Cannot read signature of %s file", basename( (char *)fname ) );
     5         kx     }
     5         kx     /* TAR */
     5         kx     if( buf[0] == 0x75 && buf[1] == 0x73 && buf[2] == 0x74 && buf[3] == 0x61 && buf[4] == 0x72 )
     5         kx     {
     5         kx       close( fd ); return IFMT_PKG;
     5         kx     }
     5         kx   }
     5         kx 
     5         kx   close( fd ); return IFMT_UNKNOWN;
     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 #if defined( HAVE_DIALOG )
     5         kx   const char* short_options = "hvacgiml:p:r:";
     5         kx #else
     5         kx   const char* short_options = "hvacgl:p:r:";
     5         kx #endif
     5         kx #else
     5         kx #if defined( HAVE_DIALOG )
     5         kx   const char* short_options = "hvaciml:p:r:";
     5         kx #else
     5         kx   const char* short_options = "hvacl:p:r:";
     5         kx #endif
     5         kx #endif
     5         kx 
     5         kx #define REINSTALL             812
     5         kx #define IGNORE_CHREFS_ERRORS  872
     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     { "always-ask",            no_argument,       NULL, 'a' },
     5         kx     { "check-requires",        no_argument,       NULL, 'c' },
     5         kx #if defined( HAVE_GPG2 )
     5         kx     { "gpg-verify",            no_argument,       NULL, 'g' },
     5         kx #endif
     5         kx     { "ignore-chrefs-errors",  no_argument,       NULL, IGNORE_CHREFS_ERRORS },
     5         kx #if defined( HAVE_DIALOG )
     5         kx     { "info-dialog",           no_argument,       NULL, 'i' },
     5         kx     { "menu-dialog",           no_argument,       NULL, 'm' },
     5         kx #endif
     5         kx     { "pkglist",               required_argument, NULL, 'l' },
     5         kx     { "priority",              required_argument, NULL, 'p' },
     5         kx     { "reinstall",             no_argument,       NULL, REINSTALL },
     5         kx     { "root",                  required_argument, NULL, 'r' },
     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       case 'a':
     5         kx       {
     5         kx         ask = 1;
     5         kx         break;
     5         kx       }
     5         kx       case 'c':
     5         kx       {
     5         kx         rqck = 1;
     5         kx         break;
     5         kx       }
     5         kx #if defined( HAVE_GPG2 )
     5         kx       case 'g':
     5         kx       {
     5         kx         gpgck = 1;
     5         kx         break;
     5         kx       }
     5         kx #endif
     5         kx 
     5         kx #if defined( HAVE_DIALOG )
     5         kx       case 'i':
     5         kx       {
     5         kx         update_mode = INFODIALOG;
     5         kx         break;
     5         kx       }
     5         kx       case 'm':
     5         kx       {
     5         kx         update_mode = MENUDIALOG;
     5         kx         break;
     5         kx       }
     5         kx #endif
     5         kx 
     5         kx       case REINSTALL:
     5         kx       {
     5         kx         reinstall = 1;
     5         kx         break;
     5         kx       }
     5         kx       case IGNORE_CHREFS_ERRORS:
     5         kx       {
     5         kx         ignore_chrefs_errors = 1;
     5         kx         break;
     5         kx       }
     5         kx 
     5         kx       case 'p':
     5         kx       {
     5         kx         if( optarg != NULL )
     5         kx         {
     5         kx           char *match = NULL;
     5         kx 
     5         kx           if( strlen( (const char *)optarg ) > 2 )
     5         kx           {
     5         kx             to_lowercase( optarg );
     5         kx             if( (match = strstr( optarg, "req" )) && match == optarg ) {
     5         kx               priority = REQUIRED;
     5         kx             }
     5         kx             else if( (match = strstr( optarg, "rec" )) && match == optarg ) {
     5         kx               priority = RECOMMENDED;
     5         kx             }
     5         kx 
     5         kx             else if( (match = strstr( optarg, "opt" )) && match == optarg ) {
     5         kx               priority = OPTIONAL;
     5         kx             }
     5         kx             else if( (match = strstr( optarg, "sk" )) && match == optarg ) {
     5         kx               priority = SKIP;
     5         kx             }
     5         kx             else {
     5         kx               FATAL_ERROR( "Unknown --priority '%s' value", optarg );
     5         kx             }
     5         kx           }
     5         kx           else
     5         kx           {
     5         kx             FATAL_ERROR( "Unknown --priority '%s' value", optarg );
     5         kx           }
     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       case 'l':
     5         kx       {
     5         kx         if( optarg != NULL )
     5         kx         {
     5         kx           pkglist_fname = 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 
     5         kx       case 'r':
     5         kx       {
     5         kx         if( optarg != NULL )
     5         kx         {
     5         kx           char cwd[PATH_MAX];
     5         kx 
     5         kx           bzero( (void *)cwd, PATH_MAX );
     5         kx           if( optarg[0] != '/' && curdir )
     5         kx           {
     5         kx             /* skip current directory definition './' at start of argument: */
     5         kx             if( !strncmp( optarg, "./", 2 ) && strncmp( optarg, "..", 2 ) )
     5         kx               (void)sprintf( cwd, "%s/%s", curdir, optarg + 2 );
     5         kx             else if( (strlen( optarg ) == 1) && !strncmp( optarg, ".", 1 ) )
     5         kx               (void)sprintf( cwd, "%s", curdir );
     5         kx             else
     5         kx               (void)sprintf( cwd, "%s/%s", curdir, optarg );
     5         kx             root = xstrdup( (const char *)cwd );
     5         kx           }
     5         kx           else
     5         kx           {
     5         kx             root = xstrdup( (const char *)optarg );
     5         kx           }
     5         kx           remove_trailing_slash( root );
     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       case '?': default:
     5         kx       {
     5         kx         usage();
     5         kx         break;
     5         kx       }
     5         kx     }
     5         kx   }
     5         kx 
     5         kx 
     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     /* absolute path to input package: */
     5         kx     if( argv[optind][0] != '/' && curdir )
     5         kx       (void)sprintf( buf, "%s/%s", curdir, (const char *)argv[optind] );
     5         kx     else
     5         kx       (void)strcpy( buf, (const char *)argv[optind] );
     5         kx 
     5         kx     if( stat( (const char *)&buf[0], &st ) == -1 )
     5         kx     {
     5         kx       FATAL_ERROR( "Cannot access '%s' file: %s", buf, strerror( errno ) );
     5         kx     }
     5         kx 
     5         kx     if( S_ISREG(st.st_mode) )
     5         kx     {
     5         kx       pkg_fname = xstrdup( (const char *)&buf[0] );
     5         kx       bind_asc_extention( buf );
     5         kx       asc_fname = xstrdup( (const char *)&buf[0] );
     5         kx       free( buf );
     5         kx     }
     5         kx     else
     5         kx     {
     5         kx       FATAL_ERROR( "Input package '%s' is not a regular file", (const char *)argv[optind] );
     5         kx     }
     5         kx   }
     5         kx   else
     5         kx   {
     5         kx     usage();
     5         kx   }
     5         kx 
     5         kx 
     5         kx   if( !pkgs_path )
     5         kx   {
     5         kx     struct stat st;
     5         kx     char  *buf = NULL;
     5         kx     int    len;
     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( !root )
     5         kx     {
     5         kx       buf[0] = '/'; buf[1] = '\0';
     5         kx       root = xstrdup( (const char *)buf );
     5         kx     }
     5         kx     else
     5         kx     {
     5         kx       len = strlen( root );
     5         kx 
     5         kx       (void)strcpy( buf, (const char *)root );
     5         kx       if( buf[ len - 1 ] != '/' )
     5         kx       {
     5         kx         buf[len] = '/'; buf[len+1] = '\0';
     5         kx         free( root ); root = xstrdup( (const char *)buf );
     5         kx       }
     5         kx     }
     5         kx 
     5         kx     if( stat( (const char *)&buf[0], &st ) == -1 )
     5         kx     {
     5         kx       FATAL_ERROR( "Cannot access '%s' file or directory: %s", buf, strerror( errno ) );
     5         kx     }
     5         kx     if( !S_ISDIR(st.st_mode) )
     5         kx     {
     5         kx       FATAL_ERROR( "Defined --root '%s' is not a directory", buf );
     5         kx     }
     5         kx 
     5         kx     len = strlen( (const char *)buf );
     5         kx 
     5         kx     (void)strcat( buf, PACKAGES_PATH );
     5         kx     if( _mkdir_p( buf, S_IRWXU | S_IRWXG | S_IRWXO ) != 0 )
     5         kx     {
     5         kx       FATAL_ERROR( "Cannot access '/%s' directory", PACKAGES_PATH );
     5         kx     }
     5         kx     pkgs_path = xstrdup( (const char *)&buf[0] );
     5         kx 
     5         kx     /*********************************************
     5         kx       Create other directories of Setup Database:
     5         kx      */
     5         kx     buf[len] = '\0';
     5         kx     (void)strcat( buf, REMOVED_PKGS_PATH );
     5         kx     if( _mkdir_p( buf, S_IRWXU | S_IRWXG | S_IRWXO ) != 0 )
     5         kx     {
     5         kx       FATAL_ERROR( "Cannot access '/%s' directory", REMOVED_PKGS_PATH );
     5         kx     }
     5         kx     rempkgs_path = xstrdup( (const char *)&buf[0] );
     5         kx 
     5         kx     buf[len] = '\0';
     5         kx     (void)strcat( buf, SETUP_PATH );
     5         kx     if( _mkdir_p( buf, S_IRWXU | S_IRWXG | S_IRWXO ) != 0 )
     5         kx     {
     5         kx       FATAL_ERROR( "Cannot access '/%s' directory", SETUP_PATH );
     5         kx     }
     5         kx 
     5         kx     /*********************************************
     5         kx       Allocate memory for Setup LOG File name:
     5         kx      */
     5         kx     buf[len] = '\0';
     5         kx     (void)strcat( buf, LOG_PATH );
     5         kx     (void)strcat( buf, SETUP_LOG_FILE );
     5         kx     log_fname = xstrdup( (const char *)&buf[0] );
     5         kx 
     5         kx     free( buf );
     5         kx 
     5         kx   } /* End if( !pkgs_path ) */
     5         kx }
     5         kx 
     5         kx static void setup_log( char *format, ... )
     5         kx {
     5         kx   FILE *fp = NULL;
     5         kx 
     5         kx   time_t     t = time( NULL );
     5         kx   struct tm tm = *localtime(&t);
     5         kx 
     5         kx   va_list argp;
     5         kx 
     5         kx   if( ! format ) return;
     5         kx 
     5         kx   fp = fopen( (const char *)log_fname, "a" );
     5         kx   if( !fp )
     5         kx   {
     5         kx     FATAL_ERROR( "Cannot open /%s%s file", LOG_PATH, SETUP_LOG_FILE );
     5         kx   }
     5         kx 
     5         kx   fprintf( fp, "[%04d-%02d-%02d %02d:%02d:%02d]: ",
     5         kx                   tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
     5         kx                                  tm.tm_hour, tm.tm_min, tm.tm_sec );
     5         kx 
     5         kx   va_start( argp, format );
     5         kx   vfprintf( fp, format, argp );
     5         kx   fprintf( fp, "\n" );
     5         kx 
     5         kx   fflush( fp );
     5         kx   fclose( fp );
     5         kx }
     5         kx 
     5         kx /***********************************************************
     5         kx   Remove leading spaces and take non-space characters only:
     5         kx   (Especialy for pkginfo lines)
     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 /*******************************
     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 char *trim( char *s )
     5         kx {
     5         kx   char *p = (char *)0;
     5         kx 
     5         kx   if( !s || *s == '\0' ) return p;
     5         kx 
     5         kx   p = s + strlen( s ) - 1;
     5         kx   while( isspace( *p ) ) { *p-- = '\0'; }
     5         kx   p = s; while( isspace( *p ) ) { ++p; }
     5         kx 
     5         kx   return( p );
     5         kx }
     5         kx 
     5         kx 
     5         kx static char *size_to_string( size_t pkgsize )
     5         kx {
     5         kx   int    nd;
     5         kx   double sz = (double)pkgsize / (double)1024;
     5         kx 
     5         kx   char  *ret = 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   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   ret = xstrdup( (const char *)&tmp[0] );
     5         kx   free( tmp );
     5         kx 
     5         kx   return ret;
     5         kx }
     5         kx 
     5         kx static void read_pkginfo( const char *pkginfo_fname )
     5         kx {
     5         kx   char *ln      = NULL;
     5         kx   char *line    = NULL;
     5         kx 
     5         kx   FILE *pkginfo = NULL;
     5         kx 
     5         kx   if( pkginfo_fname != NULL )
     5         kx   {
     5         kx     pkginfo = fopen( (const char *)pkginfo_fname, "r" );
     5         kx     if( !pkginfo )
     5         kx     {
     5         kx       FATAL_ERROR( "Cannot open %s file", pkginfo_fname );
     5         kx     }
     5         kx   }
     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 )
     5         kx       {
     5         kx         if( pkgname ) { free( pkgname ); }
     5         kx         pkgname = skip_spaces( p );
     5         kx       }
     5         kx     }
     5         kx     if( (match = strstr( ln, "pkgver" )) && match == ln ) {
     5         kx       char *p = index( match, '=' ) + 1;
     5         kx       if( p != NULL )
     5         kx       {
     5         kx         if( pkgver ) { free( pkgver ); }
     5         kx         pkgver = 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 )
     5         kx       {
     5         kx         if( arch ) { free( arch ); }
     5         kx         arch = skip_spaces( p );
     5         kx       }
     5         kx     }
     5         kx     if( (match = strstr( ln, "distroname" )) && match == ln ) {
     5         kx       char *p = index( match, '=' ) + 1;
     5         kx       if( p != NULL )
     5         kx       {
     5         kx         if( distroname ) { free( distroname ); }
     5         kx         distroname = skip_spaces( p );
     5         kx       }
     5         kx     }
     5         kx     if( (match = strstr( ln, "distrover" )) && match == ln ) {
     5         kx       char *p = index( match, '=' ) + 1;
     5         kx       if( p != NULL )
     5         kx       {
     5         kx         if( distrover ) { free( distrover ); }
     5         kx         distrover = skip_spaces( p );
     5         kx       }
     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 )
     5         kx       {
     5         kx         if( group ) { free( group ); }
     5         kx         group = skip_spaces( p );
     5         kx       }
     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           if( short_description ) { free( short_description ); }
     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 )
     5         kx       {
     5         kx         if( url ) { free( url ); }
     5         kx         url = skip_spaces( p );
     5         kx       }
     5         kx     }
     5         kx     if( (match = strstr( ln, "license" )) && match == ln ) {
     5         kx       char *p = index( match, '=' ) + 1;
     5         kx       if( p != NULL )
     5         kx       {
     5         kx         if( license ) { free( license ); }
     5         kx         license = skip_spaces( p );
     5         kx       }
     5         kx     }
     5         kx 
     5         kx     if( (match = strstr( ln, "uncompressed_size" )) && match == ln ) {
     5         kx       char *p = index( match, '=' ) + 1;
     5         kx       if( p != NULL )
     5         kx       {
     5         kx         if( uncompressed_size ) { free( uncompressed_size ); }
     5         kx         uncompressed_size = skip_spaces( p );
     5         kx       }
     5         kx     }
     5         kx     if( (match = strstr( ln, "total_files" )) && match == ln ) {
     5         kx       char *p = index( match, '=' ) + 1;
     5         kx       if( p != NULL )
     5         kx       {
     5         kx         if( total_files ) { free( total_files ); }
     5         kx         total_files = skip_spaces( p );
     5         kx       }
     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   fclose( pkginfo );
     5         kx }
     5         kx 
     5         kx 
     5         kx /***********************************************************
     5         kx   Find functions:
     5         kx  */
     5         kx static void _search_pkglog( const char *dirpath, const char *grp )
     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   char   *pname = (char *)dirpath + strlen( root ); /* do not remove leading '/' */
     5         kx 
     5         kx   if( stat( dirpath, &path_sb ) == -1 )
     5         kx   {
     5         kx     FATAL_ERROR( "%s: Cannot stat Setup Database or group directory", pname );
     5         kx   }
     5         kx 
     5         kx   if( S_ISDIR(path_sb.st_mode) == 0 )
     5         kx   {
     5         kx     FATAL_ERROR( "%s: Setup Database or group is not a directory", pname );
     5         kx   }
     5         kx 
     5         kx   if( (dir = opendir(dirpath) ) == NULL )
     5         kx   {
     5         kx     FATAL_ERROR( "Canot access %s directory: %s", pname, 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( stat( path, &entry_sb ) == 0 )
     5         kx     {
     5         kx       if( S_ISREG(entry_sb.st_mode) )
     5         kx       {
     5         kx         char *match = NULL, *name  = basename( path );
     5         kx 
     5         kx         if( (match = strstr( name, pkgname )) && match == name )
     5         kx         {
     5         kx           /****************************************************************
     5         kx             Здесь мы еще должны проверить, что найденный пакет не имеет
     5         kx             более длинное имя, которое начинается с имени искомого пакета.
     5         kx             Полагаясь на факт, что версия может начинаться только с цифры,
     5         kx             мы пропускаем символ '-', разделяющий имя и версию пакета,
     5         kx             а затем проверяем начальный символ версии:
     5         kx            */
     5         kx           if( *(name + strlen( pkgname )) == '-' && isdigit( *(name + strlen( pkgname ) + 1) ) )
     5         kx           {
     5         kx             remlog_fname = xstrdup( (const char *)path );
     5         kx             closedir( dir );
     5         kx             return;
     5         kx           }
     5         kx         }
     5         kx       }
     5         kx       if( S_ISDIR(entry_sb.st_mode) && grp == NULL )
     5         kx       {
     5         kx         /**************************************************************************
     5         kx           NOTE:
     5         kx             In the Setup Database can be only one package with the same pkgname
     5         kx             but in different groups. For example, the package named 'cairo'
     5         kx             has two instance: libs/cairo-1.14.6 and xlibs/cairo-1.14.6. During
     5         kx             system installation the package libs/cairo-1.14.6 installed first
     5         kx             and then updated by xlibs/cairo-1.14.6 and PKGLOG of libs/cairo-1.14.6
     5         kx             moved from /var/log/radix/packages to /var/log/radix/removed-packages.
     5         kx 
     5         kx             So here we have to look for the PKGLOG in all group directories:
     5         kx          */
     5         kx         _search_pkglog( (const char *)path, (const char *)entry->d_name );
     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 char *find_package( void )
     5         kx {
     5         kx   char *ret = NULL;
     5         kx 
     5         kx   _search_pkglog( (const char *)pkgs_path, NULL );
     5         kx   if( remlog_fname )
     5         kx   {
     5         kx     ret = remlog_fname;
     5         kx   }
     5         kx 
     5         kx   return ret;
     5         kx }
     5         kx /*
     5         kx   Enf of Find functions.
     5         kx  ***********************************************************/
     5         kx 
     5         kx /***********************************************************
     5         kx   check_installed_package():
     5         kx   ---------------------
     5         kx 
     5         kx     Возвращает:
     5         kx      -1 если пакет установлен, но его версия меньше
     5         kx         запрашиваемого,
     5         kx       0 если версия установленного и запрашиваемого равны,
     5         kx       1 если пакет установлен, но его версия больше
     5         kx         запрашиваемого.
     5         kx 
     5         kx     В случае возврата -1 или 1, устанавливается переменная
     5         kx     instaled_version, равная версии существующего пакета.
     5         kx 
     5         kx     Если пакет не установлен, осуществляется выход со статусом 30.
     5         kx  */
     5         kx static int check_installed_package( void )
     5         kx {
     5         kx   struct stat st;
     5         kx   char *fname = pkg_fname;
     5         kx 
     5         kx   enum _input_type  type = IFMT_UNKNOWN;
     5         kx   char              uncompress = '\0';
     5         kx 
     5         kx   char *requested_version = NULL;
     5         kx   char *requested_group   = NULL;
     5         kx 
     5         kx   int ret = 0;
     5         kx 
     5         kx   bzero( (void *)&st, sizeof( struct stat ) );
     5         kx 
     5         kx   if( stat( (const char *)fname, &st ) == -1 )
     5         kx   {
     5         kx     FATAL_ERROR( "Cannot access input '%s' file: %s", fname, strerror( errno ) );
     5         kx   }
     5         kx 
     5         kx   type = check_input_file( &uncompress, fname );
     5         kx   if( type != IFMT_PKG )
     5         kx   {
     5         kx     FATAL_ERROR( "Unknown format of input '%s' file", fname );
     5         kx   }
     5         kx 
     5         kx   if( S_ISREG(st.st_mode) )
     5         kx   {
     5         kx     pid_t p = (pid_t) -1;
     5         kx     int   rc;
     5         kx 
     5         kx     int   len = 0;
     5         kx     char *tmp= NULL, *cmd = NULL;
     5         kx 
     5         kx     tmp = (char *)malloc( (size_t)PATH_MAX );
     5         kx     if( !tmp ) { FATAL_ERROR( "Cannot allocate memory" ); }
     5         kx     bzero( (void *)tmp, PATH_MAX );
     5         kx 
     5         kx     (void)sprintf( &tmp[0], "%s", tmpdir );
     5         kx     if( _mkdir_p( tmp, S_IRWXU | S_IRWXG | S_IRWXO ) != 0 )
     5         kx     {
     5         kx       FATAL_ERROR( "Cannot get PKGINFO from '%s' file", basename( (char *)fname ) );
     5         kx     }
     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,
     5         kx                     "%s/pkginfo -d %s -o pkginfo %s > /dev/null 2>&1",
     5         kx                     selfdir, tmp, fname );
     5         kx     if( len == 0 || len == PATH_MAX - 1 )
     5         kx     {
     5         kx       FATAL_ERROR( "Cannot get PKGINFO from %s file", basename( (char *)fname ) );
     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 get PKGINFO from '%s' file", basename( (char *)fname ) );
     5         kx     }
     5         kx 
     5         kx     (void)strcat( tmp, "/.PKGINFO" );
     5         kx     read_pkginfo( (const char *)&tmp[0] );
     5         kx     (void)unlink( (const char *)&tmp[0] ); /* :remove unnecessary .PKGINFO file */
     5         kx     *(strstr( tmp, "/.PKGINFO" )) = '\0';  /* :restore 'tmpdir' in tmp[] buffer */
     5         kx 
     5         kx     requested_version = xstrdup( (const char *)pkgver );
     5         kx     requested_group   = ( group ) ? xstrdup( (const char *)group ) : NULL;
     5         kx 
     5         kx     fname = NULL;
     5         kx     fname = find_package();
     5         kx     if( !fname )
     5         kx     {
     5         kx       if( update_mode != CONSOLE )
     5         kx       {
     5         kx #if defined( HAVE_DIALOG )
     5         kx         info_pkg_box( "Update:", basename( pkg_fname ), NULL, NULL,
     5         kx                       "\nPackage is not installed.\n", 5, 0, 0 );
     5         kx #else
     5         kx         fprintf( stdout, "\nPackage '%s' is not installed.\n\n", basename( pkg_fname ) );
     5         kx #endif
     5         kx       }
     5         kx       else
     5         kx       {
     5         kx         fprintf( stdout, "Specified package '%s' is not installed.\n\n", basename( pkg_fname ) );
     5         kx       }
     5         kx 
     5         kx       exit_status = 30; /* Package is not installed: install */
     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     }
     5         kx 
     5         kx     free( cmd );
     5         kx     free( tmp );
     5         kx   }
     5         kx   else
     5         kx   {
     5         kx     FATAL_ERROR( "Input %s file is not a regular file", basename( (char *)fname ) );
     5         kx   }
     5         kx 
     5         kx 
     5         kx   /* Now fname is a name of installed PKGLOG file; check fname again: */
     5         kx   if( stat( (const char *)fname, &st ) == -1 )
     5         kx   {
     5         kx     FATAL_ERROR( "Cannot access input '%s' file: %s", fname, strerror( errno ) );
     5         kx   }
     5         kx 
     5         kx   type = check_input_file( &uncompress, fname );
     5         kx   if( type != IFMT_LOG )
     5         kx   {
     5         kx     FATAL_ERROR( "Unknown format of input '%s' file", fname );
     5         kx   }
     5         kx 
     5         kx   if( S_ISREG(st.st_mode) )
     5         kx   {
     5         kx     pid_t p = (pid_t) -1;
     5         kx     int   rc;
     5         kx 
     5         kx     int   len = 0;
     5         kx     char *tmp= NULL, *cmd = NULL;
     5         kx 
     5         kx     tmp = (char *)malloc( (size_t)PATH_MAX );
     5         kx     if( !tmp ) { FATAL_ERROR( "Cannot allocate memory" ); }
     5         kx     bzero( (void *)tmp, PATH_MAX );
     5         kx 
     5         kx     (void)sprintf( &tmp[0], "%s/to-remove", tmpdir );
     5         kx     if( _mkdir_p( tmp, S_IRWXU | S_IRWXG | S_IRWXO ) != 0 )
     5         kx     {
     5         kx       FATAL_ERROR( "Cannot get PKGINFO from '%s' file", basename( (char *)fname ) );
     5         kx     }
     5         kx     rtmpdir = xstrdup( (const char *)&tmp[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,
     5         kx                     "%s/pkginfo -d %s -o pkginfo,description,restore-links,filelist %s > /dev/null 2>&1",
     5         kx                     selfdir, tmp, fname );
     5         kx     if( len == 0 || len == PATH_MAX - 1 )
     5         kx     {
     5         kx       FATAL_ERROR( "Cannot get PKGINFO from %s file", basename( (char *)fname ) );
     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 get PKGINFO from '%s' file", basename( (char *)fname ) );
     5         kx     }
     5         kx 
     5         kx     (void)strcat( tmp, "/.PKGINFO" );
     5         kx     read_pkginfo( (const char *)&tmp[0] );
     5         kx     *(strstr( tmp, "/.PKGINFO" )) = '\0'; /* :restore tmpdir in tmp[] buffer */
     5         kx 
     5         kx     installed_version = xstrdup( (const char *)pkgver );
     5         kx     installed_group   = ( group ) ? xstrdup( (const char *)group ) : NULL;
     5         kx 
     5         kx     free( cmd );
     5         kx     free( tmp );
     5         kx 
     5         kx     if( requested_group && installed_group )
     5         kx     {
     5         kx       if( !(ret = strcmp( (const char *)installed_group, (const char *)requested_group )) )
     5         kx       {
     5         kx         ret = cmp_version( (const char *)installed_version, (const char *)requested_version );
     5         kx       }
     5         kx     }
     5         kx     else if( !requested_group && !installed_group )
     5         kx     {
     5         kx       ret = cmp_version( (const char *)installed_version, (const char *)requested_version );
     5         kx     }
     5         kx     else if( requested_group )
     5         kx     {
     5         kx       ret = -1;
     5         kx     }
     5         kx     else
     5         kx     {
     5         kx       ret = 1;
     5         kx     }
     5         kx 
     5         kx     if( requested_version ) { free( requested_version ); }
     5         kx     if( requested_group )   { free( requested_group );   }
     5         kx   }
     5         kx   else
     5         kx   {
     5         kx     FATAL_ERROR( "Input %s file is not a regular file", basename( (char *)fname ) );
     5         kx   }
     5         kx 
     5         kx   return ret;
     5         kx }
     5         kx 
     5         kx static int check_installed_pkg_integrity( void )
     5         kx {
     5         kx   struct stat st;
     5         kx   FILE  *fp = NULL;
     5         kx 
     5         kx   char *ln   = NULL;
     5         kx   char *line = NULL;
     5         kx 
     5         kx   char *buf = NULL, *tmp = NULL;
     5         kx 
     5         kx   int restore_links = 0;
     5         kx   int ret = 1;
     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   tmp = (char *)malloc( (size_t)PATH_MAX );
     5         kx   if( !tmp ) { FATAL_ERROR( "Cannot allocate memory" ); }
     5         kx   bzero( (void *)tmp, PATH_MAX );
     5         kx 
     5         kx   /* Check if .RESTORELINKS is present */
     5         kx   (void)sprintf( &tmp[0], "%s/.RESTORELINKS", rtmpdir );
     5         kx   bzero( (void *)&st, sizeof( struct stat ) );
     5         kx   if( (stat( (const char *)&tmp[0], &st ) == 0) && (st.st_size > 8) )
     5         kx   {
     5         kx     restore_links = 1;
     5         kx   }
     5         kx 
     5         kx   (void)sprintf( &tmp[0], "%s/.FILELIST", rtmpdir );
     5         kx   fp = fopen( (const char *)&tmp[0], "r" );
     5         kx   if( !fp )
     5         kx   {
     5         kx     FATAL_ERROR( "Cannot open .FILELIST file" );
     5         kx   }
     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   while( (ln = fgets( line, PATH_MAX, fp )) )
     5         kx   {
     5         kx     int dir = 0;
     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( *(ln + strlen(ln) - 1) == '/' ) { dir = 1; *(ln + strlen(ln) - 1) = '\0'; }
     5         kx     else                                { dir = 0; }
     5         kx 
     5         kx     if( !dir )
     5         kx     {
     5         kx       char *p = rindex( ln, '.' );
     5         kx       if( p && !strncmp( (const char *)p, ".new", 4 ) )
     5         kx       {
     5         kx         /**************************
     5         kx           Do not check .new files:
     5         kx          */
     5         kx         *p = '\0';
     5         kx       }
     5         kx     }
     5         kx 
     5         kx     (void)sprintf( &buf[0], "%s%s", root, ln );
     5         kx     bzero( (void *)&st, sizeof( struct stat ) );
     5         kx 
     5         kx     if( lstat( (const char *)&buf[0], &st ) == -1 )
     5         kx     {
     5         kx       /* cannot access file list entry */
     5         kx       ret = 0; continue;
     5         kx     }
     5         kx 
     5         kx     if( dir )
     5         kx     {
     5         kx       if( S_ISDIR(st.st_mode) == 0 )
     5         kx       {
     5         kx         /* not a directory */
     5         kx         ret = 0; continue;
     5         kx       }
     5         kx     }
     5         kx     else
     5         kx     {
     5         kx       if( S_ISREG(st.st_mode) == 0 )
     5         kx       {
     5         kx         /* not a regular file */
     5         kx         ret = 0; continue;
     5         kx       }
     5         kx       if( !restore_links )
     5         kx       {
     5         kx         if( S_ISLNK(st.st_mode) == 0 )
     5         kx         {
     5         kx           /* not a symbolic link */
     5         kx           ret = 0; continue;
     5         kx         }
     5         kx       }
     5         kx     }
     5         kx   } /* End of while( file list entry ) */
     5         kx   fclose( fp );
     5         kx 
     5         kx 
     5         kx   (void)sprintf( &tmp[0], "%s/.RESTORELINKS", rtmpdir );
     5         kx   bzero( (void *)&st, sizeof( struct stat ) );
     5         kx 
     5         kx   if( stat( (const char *)&tmp[0], &st ) == 0 )
     5         kx   {
     5         kx     fp = fopen( (const char *)&tmp[0], "r" );
     5         kx     if( !fp )
     5         kx     {
     5         kx       FATAL_ERROR( "Cannot open .RESTORELINKS file" );
     5         kx     }
     5         kx 
     5         kx     while( (ln = fgets( line, PATH_MAX, fp )) )
     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, "; rm -rf " )) )
     5         kx       {
     5         kx         char *q = NULL;
     5         kx         char *p = strstr( ln, "cd" ) + 2;
     5         kx         char *f = strstr( ln, "; rm -rf" ) + 8;
     5         kx 
     5         kx         if( !p || !f ) continue;
     5         kx 
     5         kx         while( (*p == ' ' || *p == '\t') && *p != '\0' ) ++p;
     5         kx         while( (*f == ' ' || *f == '\t') && *f != '\0' ) ++f;
     5         kx 
     5         kx         q = p; while( *q != ' ' && *q != '\t' && *q != ';' && *q != '\0' ) ++q; *q = '\0';
     5         kx         q = f; while( *q != ' ' && *q != '\t' && *q != ';' && *q != '\0' ) ++q; *q = '\0';
     5         kx 
     5         kx         if( p && f )
     5         kx         {
     5         kx           (void)sprintf( &buf[0], "%s/%s/%s", root, p, f );
     5         kx           bzero( (void *)&st, sizeof( struct stat ) );
     5         kx 
     5         kx           if( lstat( (const char *)&buf[0], &st ) == -1 )
     5         kx           {
     5         kx             /* cannot access restore links entry */
     5         kx             ret = 0; continue;
     5         kx           }
     5         kx 
     5         kx           if( S_ISLNK(st.st_mode) == 0 )
     5         kx           {
     5         kx             /* not a symbolic link */
     5         kx             ret = 0; continue;
     5         kx           }
     5         kx         }
     5         kx       }
     5         kx     } /* End of while( restore links entry ) */
     5         kx     fclose( fp );
     5         kx   }
     5         kx 
     5         kx   free( line );
     5         kx   free( tmp );
     5         kx   free( buf );
     5         kx 
     5         kx   return ret;
     5         kx }
     5         kx 
     5         kx 
     5         kx static void read_service_files( void )
     5         kx {
     5         kx   struct stat st;
     5         kx   char *fname = pkg_fname;
     5         kx 
     5         kx   enum _input_type  type = IFMT_UNKNOWN;
     5         kx 
     5         kx   bzero( (void *)&st, sizeof( struct stat ) );
     5         kx 
     5         kx   if( stat( (const char *)fname, &st ) == -1 )
     5         kx   {
     5         kx     FATAL_ERROR( "Cannot access input '%s' file: %s", fname, strerror( errno ) );
     5         kx   }
     5         kx 
     5         kx   type = check_input_file( &uncompress, fname );
     5         kx   if( type != IFMT_PKG )
     5         kx   {
     5         kx     FATAL_ERROR( "Unknown format of input '%s' file", fname );
     5         kx   }
     5         kx 
     5         kx   if( S_ISREG(st.st_mode) )
     5         kx   {
     5         kx     pid_t p = (pid_t) -1;
     5         kx     int   rc;
     5         kx 
     5         kx     int   len = 0;
     5         kx     char *tmp= NULL, *cmd = NULL;
     5         kx 
     5         kx     tmp = (char *)malloc( (size_t)PATH_MAX );
     5         kx     if( !tmp ) { FATAL_ERROR( "Cannot allocate memory" ); }
     5         kx     bzero( (void *)tmp, PATH_MAX );
     5         kx 
     5         kx     (void)sprintf( &tmp[0], "%s", tmpdir );
     5         kx     if( _mkdir_p( tmp, S_IRWXU | S_IRWXG | S_IRWXO ) != 0 )
     5         kx     {
     5         kx       FATAL_ERROR( "Cannot get PKGINFO from '%s' file", basename( (char *)fname ) );
     5         kx     }
     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,
     5         kx                     "%s/pkginfo -d %s"
     5         kx                     " -o pkginfo,description,requires,restore-links,install-script,filelist"
     5         kx                     " %s > /dev/null 2>&1",
     5         kx                     selfdir, tmp, fname );
     5         kx     if( len == 0 || len == PATH_MAX - 1 )
     5         kx     {
     5         kx       FATAL_ERROR( "Cannot get PKGINFO from %s file", basename( (char *)fname ) );
     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 get PKGINFO from '%s' file", basename( (char *)fname ) );
     5         kx     }
     5         kx 
     5         kx     (void)strcat( tmp, "/.PKGINFO" );
     5         kx     read_pkginfo( (const char *)&tmp[0] );
     5         kx     *(strstr( tmp, "/.PKGINFO" )) = '\0'; /* :restore tmpdir in tmp[] buffer */
     5         kx 
     5         kx     compressed_size = size_to_string( st.st_size );
     5         kx 
     5         kx     /******************
     5         kx       Get PKGLOG file:
     5         kx      */
     5         kx     len = snprintf( &cmd[0], PATH_MAX,
     5         kx                     "%s/pkglog -m -d %s %s  > /dev/null 2>&1",
     5         kx                     selfdir, tmp, tmp );
     5         kx     if( len == 0 || len == PATH_MAX - 1 )
     5         kx     {
     5         kx       FATAL_ERROR( "Cannot get PKGLOG from %s file", basename( (char *)fname ) );
     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 get PKGLOG from '%s' file", basename( (char *)fname ) );
     5         kx     }
     5         kx 
     5         kx     if( group )
     5         kx       (void)sprintf( cmd, "%s/%s/%s-%s-%s-%s-%s", tmp, group, pkgname, pkgver, arch, distroname, distrover );
     5         kx     else
     5         kx       (void)sprintf( cmd, "%s/%s-%s-%s-%s-%s", tmp, pkgname, pkgver, arch, distroname, distrover );
     5         kx 
     5         kx     bzero( (void *)&st, sizeof( struct stat ) );
     5         kx     if( stat( (const char *)cmd, &st ) == -1 )
     5         kx     {
     5         kx       FATAL_ERROR( "Cannot get PKGLOG from '%s' file: %s", basename( (char *)fname ), strerror( errno ) );
     5         kx     }
     5         kx 
     5         kx     pkglog_fname = xstrdup( (const char *)cmd );
     5         kx 
     5         kx     /*************************************
     5         kx       Attempt to read packages list file:
     5         kx      */
     5         kx     {
     5         kx       if( !pkglist_fname )
     5         kx       {
     5         kx         /*****************************************
     5         kx           Get source packages path if applicable:
     5         kx          */
     5         kx         (void)strcpy( cmd, (const char *)fname );
     5         kx         (void)strcpy( tmp, dirname( cmd ) );
     5         kx 
     5         kx         if( group && !strcmp( group, basename( tmp ) ) )
     5         kx           (void)strcpy( cmd, (const char *)dirname( tmp ) );
     5         kx         else
     5         kx           (void)strcpy( cmd, (const char *)tmp );
     5         kx 
     5         kx         /*****************************************
     5         kx           Save default packages list file name:
     5         kx          */
     5         kx         (void)strcat( cmd, "/.pkglist" );
     5         kx         pkglist_fname = xstrdup( (const char *)cmd );
     5         kx       }
     5         kx 
     5         kx       /**************************
     5         kx         read .pkglist if exists:
     5         kx        */
     5         kx       bzero( (void *)&st, sizeof( struct stat ) );
     5         kx       if( (stat( (const char *)pkglist_fname, &st ) == 0) && S_ISREG(st.st_mode) )
     5         kx       {
     5         kx         char *ln      = NULL;
     5         kx         char *line    = NULL;
     5         kx 
     5         kx         FILE *pkglist = NULL;
     5         kx 
     5         kx         pkglist = fopen( (const char *)pkglist_fname, "r" );
     5         kx         if( !pkglist )
     5         kx         {
     5         kx           FATAL_ERROR( "Cannot open %s file", pkglist_fname );
     5         kx         }
     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, pkglist )) )
     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           {
     5         kx             char *p = NULL;
     5         kx             char *name = NULL, *vers = NULL, *desc = NULL, *ball = NULL, *proc = NULL, *prio = NULL;
     5         kx 
     5         kx             name = ln;
     5         kx             if( (p = index( (const char *)name, ':' )) ) { *p = '\0'; vers = ++p; name = trim( name ); } else continue;
     5         kx             if( (p = index( (const char *)vers, ':' )) ) { *p = '\0'; desc = ++p; vers = trim( vers ); } else continue;
     5         kx             if( (p = index( (const char *)desc, ':' )) ) { *p = '\0'; ball = ++p; desc = trim( desc ); } else continue;
     5         kx             if( (p = index( (const char *)ball, ':' )) ) { *p = '\0'; proc = ++p; ball = trim( ball ); } else continue;
     5         kx             if( (p = index( (const char *)proc, ':' )) ) { *p = '\0'; prio = ++p; proc = trim( proc ); } else continue;
     5         kx             prio = trim( prio );
     5         kx 
     5         kx             if( name && vers && desc && ball && proc && prio )
     5         kx             {
     5         kx               char *grp = index( (const char *)ball, '/' );
     5         kx               if( grp )
     5         kx               {
     5         kx                 *grp = '\0'; grp = ball; grp = trim( grp );
     5         kx                 if( strcmp( group, grp ) ) continue;
     5         kx               }
     5         kx 
     5         kx               /* read priority: */
     5         kx               if( strlen( (const char*)prio ) > 2 )
     5         kx               {
     5         kx                 char *m = NULL;
     5         kx 
     5         kx                 to_lowercase( prio );
     5         kx                 if( (m = strstr( prio, "req" )) && m == prio ) {
     5         kx                   priority = REQUIRED;
     5         kx                 }
     5         kx                 else if( (m = strstr( prio, "rec" )) && m == prio ) {
     5         kx                   priority = RECOMMENDED;
     5         kx                 }
     5         kx                 else if( (m = strstr( prio, "opt" )) && m == prio ) {
     5         kx                   priority = OPTIONAL;
     5         kx                 }
     5         kx                 else if( (m = strstr( prio, "sk" )) && m == prio ) {
     5         kx                   priority = SKIP;
     5         kx                 }
     5         kx                 else {
     5         kx                   priority = REQUIRED;
     5         kx                 }
     5         kx               }
     5         kx               else
     5         kx               {
     5         kx                 priority = REQUIRED;
     5         kx               }
     5         kx 
     5         kx               /* read procedure: */
     5         kx               if( strlen( (const char*)proc ) > 5 )
     5         kx               {
     5         kx                 char *m = NULL;
     5         kx 
     5         kx                 to_lowercase( proc );
     5         kx                 if( (m = strstr( proc, "install" )) && m == proc ) {
     5         kx                   procedure = INSTALL;
     5         kx                 }
     5         kx                 else if( (m = strstr( proc, "update" )) && m == proc ) {
     5         kx                   procedure = UPDATE;
     5         kx                 }
     5         kx                 else {
     5         kx                   procedure = UPDATE;
     5         kx                 }
     5         kx               }
     5         kx               else
     5         kx               {
     5         kx                 procedure = UPDATE;
     5         kx               }
     5         kx             }
     5         kx 
     5         kx           } /* End if( match ) */
     5         kx 
     5         kx         } /* End of while( ln = fgets() ) */
     5         kx 
     5         kx         free( line );
     5         kx         fclose( pkglist );
     5         kx 
     5         kx       } /* End of reading .pkglist */
     5         kx 
     5         kx     } /* End of attemption of reading .pkflist file */
     5         kx 
     5         kx     free( cmd );
     5         kx     free( tmp );
     5         kx 
     5         kx     if( priority == SKIP )
     5         kx     {
     5         kx       exit_status = 41;
     5         kx 
     5         kx       if( update_mode != CONSOLE )
     5         kx       {
     5         kx #if defined( HAVE_DIALOG )
     5         kx         char *tmp = NULL;
     5         kx 
     5         kx         tmp = (char *)malloc( (size_t)PATH_MAX );
     5         kx         if( !tmp ) { FATAL_ERROR( "Cannot allocate memory" ); }
     5         kx         bzero( (void *)tmp, PATH_MAX );
     5         kx 
     5         kx         (void)sprintf( &tmp[0],
     5         kx                        "\nUpdate procedure is skipped due to specified '%s' priority.\n",
     5         kx                        strprio( priority, 0 ) );
     5         kx 
     5         kx         info_pkg_box( "Update:", pkgname, pkgver, strprio( priority, 0 ),
     5         kx                       (const char *)&tmp[0], 5, 0, 0 );
     5         kx 
     5         kx         free( tmp );
     5         kx #else
     5         kx         fprintf( stdout,
     5         kx                  "\nUpdate procedure of package '%s-%s' is skipped due to specified '%s' priority.\n\n",
     5         kx                  pkgname, pkgver, strprio( priority, 0 ) );
     5         kx #endif
     5         kx       }
     5         kx       else
     5         kx       {
     5         kx         fprintf( stdout,
     5         kx                  "\nUpdate procedure of package '%s-%s' is skipped due to specified '%s' priority.\n\n",
     5         kx                  pkgname, pkgver, strprio( priority, 0 ) );
     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     }
     5         kx 
     5         kx     if( procedure != UPDATE )
     5         kx     {
     5         kx       exit_status = 42;
     5         kx 
     5         kx       if( update_mode != CONSOLE )
     5         kx       {
     5         kx #if defined( HAVE_DIALOG )
     5         kx         char *tmp = NULL;
     5         kx 
     5         kx         tmp = (char *)malloc( (size_t)PATH_MAX );
     5         kx         if( !tmp ) { FATAL_ERROR( "Cannot allocate memory" ); }
     5         kx         bzero( (void *)tmp, PATH_MAX );
     5         kx 
     5         kx         (void)sprintf( &tmp[0],
     5         kx                        "\nUpdate is skipped because the '%s' procedure is specified.\n",
     5         kx                        strproc( procedure ) );
     5         kx 
     5         kx         info_pkg_box( "Update:", pkgname, pkgver, strprio( priority, 0 ),
     5         kx                       (const char *)&tmp[0], 6, 0, 0 );
     5         kx 
     5         kx         free( tmp );
     5         kx #else
     5         kx         fprintf( stdout,
     5         kx                  "\nUpdate procedure of package '%s-%s' is skipped because the '%s' procedure is specified.\n\n",
     5         kx                  pkgname, pkgver, strproc( procedure ) );
     5         kx #endif
     5         kx       }
     5         kx       else
     5         kx       {
     5         kx         fprintf( stdout,
     5         kx                  "\nUpdate procedure of package '%s-%s' is skipped because the '%s' procedure is specified.\n\n",
     5         kx                  pkgname, pkgver, strproc( procedure ) );
     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     }
     5         kx 
     5         kx   }
     5         kx   else
     5         kx   {
     5         kx     FATAL_ERROR( "Input %s file is not a regular file", basename( (char *)fname ) );
     5         kx   }
     5         kx }
     5         kx 
     5         kx 
     5         kx static void check_requires( void )
     5         kx {
     5         kx   pid_t p = (pid_t) -1;
     5         kx   int   rc;
     5         kx 
     5         kx   int   len = 0;
     5         kx   char *cmd = NULL;
     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,
     5         kx                   "%s/check-requires --root=%s %s > /dev/null 2>&1",
     5         kx                   selfdir, root, pkglog_fname );
     5         kx   if( len == 0 || len == PATH_MAX - 1 )
     5         kx   {
     5         kx     FATAL_ERROR( "Cannot check required packages for '%s-%s' package", pkgname, pkgver );
     5         kx   }
     5         kx   p = sys_exec_command( cmd );
     5         kx   rc = sys_wait_command( p, (char *)NULL, PATH_MAX );
     5         kx 
     5         kx   free( cmd );
     5         kx 
     5         kx   if( rc != 0 )
     5         kx   {
     5         kx     if( update_mode != CONSOLE )
     5         kx     {
     5         kx #if defined( HAVE_DIALOG )
     5         kx       info_pkg_box( "Update:", pkgname, pkgver, strprio( priority, 0 ),
     5         kx                     "\nPackage requires other packages to be installed.\n", 5, 0, 0 );
     5         kx #else
     5         kx       fprintf( stdout, "\nPackage '%s-%s' requires other packages to be installed.\n\n", pkgname, pkgver );
     5         kx #endif
     5         kx     }
     5         kx     else
     5         kx     {
     5         kx       fprintf( stdout, "\nPackage '%s-%s' requires other packages to be installed.\n\n", pkgname, pkgver );
     5         kx     }
     5         kx 
     5         kx     if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); }
     5         kx     free_resources();
     5         kx     exit( rc );
     5         kx   }
     5         kx }
     5         kx 
     5         kx /********************************************************
     5         kx   Read .FILELIST and .RESTORELINKS functions used for
     5         kx   roolback/remove in case post-update errors:
     5         kx  */
     5         kx static int __cmp_list_items( const void *a, const void *b )
     5         kx {
     5         kx   if( a && b )
     5         kx     return strcmp( (const char *)a, (const char *)b );
     5         kx   else if( a )
     5         kx     return 1;
     5         kx   else
     5         kx     return -1;
     5         kx }
     5         kx 
     5         kx static void __free_list( void *data, void *user_data )
     5         kx {
     5         kx   if( data ) { free( data ); }
     5         kx }
     5         kx 
     5         kx static void free_list( struct dlist *list )
     5         kx {
     5         kx   if( list ) { dlist_free( list, __free_list ); }
     5         kx }
     5         kx 
     5         kx ////////////////////////////////////////////////////
     5         kx //static void __print_list( void *data, void *user_data )
     5         kx //{
     5         kx //  int *counter = (int *)user_data;
     5         kx //
     5         kx //  if( counter ) { fprintf( stdout, "item[%.5d]: %s\n", *counter, (char *)data ); ++(*counter); }
     5         kx //  else          { fprintf( stdout, "item: %s\n", (char *)data ); }
     5         kx //}
     5         kx //
     5         kx //static void print_list( struct dlist *list )
     5         kx //{
     5         kx //  int cnt = 0;
     5         kx //  if( list ) { dlist_foreach( list, __print_list, (void *)&cnt ); }
     5         kx //}
     5         kx ////////////////////////////////////////////////////
     5         kx 
     5         kx static void read_filelist( void **d, void **f, const char *path )
     5         kx {
     5         kx   struct stat st;
     5         kx   FILE  *fp = NULL;
     5         kx 
     5         kx   char *ln   = NULL;
     5         kx   char *line = NULL, *tmp = NULL;
     5         kx 
     5         kx   struct dlist *dirs  = (struct dlist *)(*d);
     5         kx   struct dlist *files = (struct dlist *)(*f);
     5         kx 
     5         kx   if( !dirs || !files || !path ) return;
     5         kx 
     5         kx   tmp = (char *)malloc( (size_t)PATH_MAX );
     5         kx   if( !tmp ) { FATAL_ERROR( "Cannot allocate memory" ); }
     5         kx   bzero( (void *)tmp, PATH_MAX );
     5         kx 
     5         kx   (void)sprintf( &tmp[0], "%s/.FILELIST", path );
     5         kx   bzero( (void *)&st, sizeof( struct stat ) );
     5         kx   if( (stat( (const char *)&tmp[0], &st ) == -1) )
     5         kx   {
     5         kx     FATAL_ERROR( "Cannot get .FILELIST from '%s' file", basename( (char *)pkglog_fname ) );
     5         kx   }
     5         kx 
     5         kx   fp = fopen( (const char *)&tmp[0], "r" );
     5         kx   if( !fp )
     5         kx   {
     5         kx     FATAL_ERROR( "Cannot open .FILELIST file" );
     5         kx   }
     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   while( (ln = fgets( line, PATH_MAX, fp )) )
     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( *(ln + strlen(ln) - 1) == '/' )
     5         kx     {
     5         kx       *(ln + strlen(ln) - 1) = '\0';
     5         kx       (void)sprintf( &tmp[0], "%s%s", (const char *)root, (const char *)ln );
     5         kx       dirs = dlist_append( dirs, xstrdup( (const char *)&tmp[0] ) );
     5         kx     }
     5         kx     else
     5         kx     {
     5         kx       (void)sprintf( &tmp[0], "%s%s", (const char *)root, (const char *)ln );
     5         kx       files = dlist_append( files, xstrdup( (const char *)&tmp[0] ) );
     5         kx     }
     5         kx 
     5         kx   } /* End of while( file list entry ) */
     5         kx 
     5         kx   fclose( fp );
     5         kx 
     5         kx   free( line );
     5         kx   free( tmp );
     5         kx }
     5         kx 
     5         kx static void read_restorelinks( void **l, const char *path )
     5         kx {
     5         kx   struct stat st;
     5         kx   FILE  *fp = NULL;
     5         kx 
     5         kx   char *ln   = NULL;
     5         kx   char *line = NULL, *tmp = NULL;
     5         kx 
     5         kx   struct dlist *links = (struct dlist *)(*l);
     5         kx 
     5         kx   if( !links || !path ) return;
     5         kx 
     5         kx   tmp = (char *)malloc( (size_t)PATH_MAX );
     5         kx   if( !tmp ) { FATAL_ERROR( "Cannot allocate memory" ); }
     5         kx   bzero( (void *)tmp, PATH_MAX );
     5         kx 
     5         kx   (void)sprintf( &tmp[0], "%s/.RESTORELINKS", path );
     5         kx   bzero( (void *)&st, sizeof( struct stat ) );
     5         kx   if( (stat( (const char *)&tmp[0], &st ) == -1) || (st.st_size < 8) )
     5         kx   {
     5         kx     free( tmp );
     5         kx     return;
     5         kx   }
     5         kx 
     5         kx   fp = fopen( (const char *)&tmp[0], "r" );
     5         kx   if( !fp )
     5         kx   {
     5         kx     FATAL_ERROR( "Cannot open .RESTORELINKS file" );
     5         kx   }
     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   while( (ln = fgets( line, PATH_MAX, fp )) )
     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, "; rm -rf " )) )
     5         kx     {
     5         kx       char *q = NULL;
     5         kx       char *p = strstr( ln, "cd" ) + 2;
     5         kx       char *f = strstr( ln, "; rm -rf" ) + 8;
     5         kx 
     5         kx       if( !p || !f ) continue;
     5         kx 
     5         kx       while( (*p == ' ' || *p == '\t') && *p != '\0' ) ++p;
     5         kx       while( (*f == ' ' || *f == '\t') && *f != '\0' ) ++f;
     5         kx 
     5         kx       q = p; while( *q != ' ' && *q != '\t' && *q != ';' && *q != '\0' ) ++q; *q = '\0';
     5         kx       q = f; while( *q != ' ' && *q != '\t' && *q != ';' && *q != '\0' ) ++q; *q = '\0';
     5         kx 
     5         kx       if( p && f )
     5         kx       {
     5         kx         (void)sprintf( &tmp[0], "%s%s/%s", (const char *)root, p, f );
     5         kx         links = dlist_append( links, xstrdup( (const char *)&tmp[0] ) );
     5         kx       }
     5         kx     }
     5         kx   } /* End of while( restore links entry ) */
     5         kx 
     5         kx   fclose( fp );
     5         kx 
     5         kx   free( line );
     5         kx   free( tmp );
     5         kx }
     5         kx /*
     5         kx   End of read .FILELIST and .RESTORELINKS functions.
     5         kx  ********************************************************/
     5         kx 
     5         kx /********************************************************
     5         kx   Rollback/Remove functions:
     5         kx  */
     5         kx static void __remove_link( void *data, void *user_data )
     5         kx {
     5         kx   const char *fname = (const char *)data;
     5         kx 
     5         kx   if( fname )
     5         kx   {
     5         kx     (void)unlink( fname );
     5         kx   }
     5         kx }
     5         kx 
     5         kx static void __remove_file( void *data, void *user_data )
     5         kx {
     5         kx   const char *fname = (const char *)data;
     5         kx 
     5         kx   if( fname )
     5         kx   {
     5         kx     char *p = rindex( fname, '.' );
     5         kx     /*
     5         kx       Если .new файл остался с тем же именем, это значит что до инсталляции
     5         kx       в системе существовал такой же файл но без расширения .new и при этом
     5         kx       он отличался от нового. В данном случае надо удалять только файл .new.
     5         kx 
     5         kx       Если же файл .new не существует, то надо удалять такой же файл но без
     5         kx       расширения .new .
     5         kx      */
     5         kx     if( p && !strncmp( (const char *)p, ".new", 4 ) )
     5         kx     {
     5         kx       struct stat st;
     5         kx 
     5         kx       bzero( (void *)&st, sizeof( struct stat ) );
     5         kx       if( (stat( fname, &st ) == -1) ) *p = '\0';
     5         kx     }
     5         kx 
     5         kx     (void)unlink( fname );
     5         kx   }
     5         kx }
     5         kx 
     5         kx static int is_dir_empty( const char *dirpath )
     5         kx {
     5         kx   int ret = 0;
     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 )   return ret; /* stat returns error code; errno is set */
     5         kx   if( S_ISDIR(path_sb.st_mode) == 0 )     return ret; /* dirpath is not a directory */
     5         kx   if( (dir = opendir(dirpath) ) == NULL ) return ret; /* Cannot open direcroty; errno is set */
     5         kx 
     5         kx   ret = 1;
     5         kx 
     5         kx   len = strlen( dirpath );
     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     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       ret = 0;
     5         kx       break;
     5         kx     }
     5         kx     /* else { stat() returns error code; errno is set; and we have to continue the loop } */
     5         kx   }
     5         kx   closedir( dir );
     5         kx 
     5         kx   return ret;
     5         kx }
     5         kx 
     5         kx static void __remove_dir( void *data, void *user_data )
     5         kx {
     5         kx   const char *dname = (const char *)data;
     5         kx 
     5         kx   if( dname && is_dir_empty( (const char *)dname ) )
     5         kx   {
     5         kx     (void)rmdir( dname );
     5         kx   }
     5         kx }
     5         kx 
     5         kx static void rollback( void )
     5         kx {
     5         kx   /* Try to change CWD to the ROOT directory: */
     5         kx   (void)chdir( (const char *)root );
     5         kx 
     5         kx   if( links ) { dlist_foreach( links, __remove_link, NULL ); }
     5         kx 
     5         kx   if( files ) { dlist_foreach( files, __remove_file, NULL ); }
     5         kx 
     5         kx   if( dirs )
     5         kx   {
     5         kx     dirs = dlist_sort( dirs, __cmp_list_items );
     5         kx     dirs = dlist_reverse( dirs );
     5         kx     dlist_foreach( dirs, __remove_dir, NULL );
     5         kx   }
     5         kx 
     5         kx   /* Try to remove PKGLOG file */
     5         kx   {
     5         kx     char *tmp = NULL;
     5         kx 
     5         kx     tmp = (char *)malloc( PATH_MAX );
     5         kx     if( tmp )
     5         kx     {
     5         kx       const char *fname = basename( (char *)pkglog_fname );
     5         kx 
     5         kx       bzero( (void *)tmp, PATH_MAX );
     5         kx 
     5         kx       if( group )
     5         kx         (void)sprintf( &tmp[0], "%s/%s/%s", pkgs_path, group, fname );
     5         kx       else
     5         kx         (void)sprintf( &tmp[0], "%s/%s", pkgs_path, fname );
     5         kx 
     5         kx       (void)unlink( (const char *)&tmp[0] );
     5         kx 
     5         kx       if( group )
     5         kx       {
     5         kx         const char *dir = (const char *)dirname( (char *)&tmp[0] );
     5         kx         if( is_dir_empty( dir ) )
     5         kx         {
     5         kx           (void)rmdir( dir );
     5         kx         }
     5         kx       }
     5         kx       free( tmp );
     5         kx     }
     5         kx   }
     5         kx 
     5         kx   /* Try to change CWD to the CURRENT directory: */
     5         kx   (void)chdir( (const char *)curdir );
     5         kx }
     5         kx 
     5         kx static void remove_package( void )
     5         kx {
     5         kx   /* Try to change CWD to the ROOT directory: */
     5         kx   (void)chdir( (const char *)root );
     5         kx 
     5         kx   if( rlinks ) { dlist_foreach( rlinks, __remove_link, NULL ); }
     5         kx 
     5         kx   if( rfiles ) { dlist_foreach( rfiles, __remove_file, NULL ); }
     5         kx 
     5         kx   if( rdirs )
     5         kx   {
     5         kx     rdirs = dlist_sort( rdirs, __cmp_list_items );
     5         kx     rdirs = dlist_reverse( rdirs );
     5         kx     dlist_foreach( rdirs, __remove_dir, NULL );
     5         kx   }
     5         kx 
     5         kx   /* Try to change CWD to the CURRENT directory: */
     5         kx   (void)chdir( (const char *)curdir );
     5         kx }
     5         kx 
     5         kx static void finalize_removal( void )
     5         kx {
     5         kx   pid_t p = (pid_t) -1;
     5         kx   int   rc;
     5         kx 
     5         kx   int   len = 0;
     5         kx   char *cmd = NULL, *tmp = NULL;
     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   tmp = (char *)malloc( (size_t)PATH_MAX );
     5         kx   if( !tmp ) { FATAL_ERROR( "Cannot allocate memory" ); }
     5         kx   bzero( (void *)tmp, PATH_MAX );
     5         kx 
     5         kx   /*********************************************
     5         kx     Decrement references in the Setup Database:
     5         kx    */
     5         kx   if( installed_group )
     5         kx     len = snprintf( &cmd[0], PATH_MAX,
     5         kx                     "%s/chrefs --operation=dec --destination=%s %s/%s > /dev/null 2>&1",
     5         kx                     selfdir, pkgs_path, installed_group, basename( (char *)remlog_fname ) );
     5         kx   else
     5         kx     len = snprintf( &cmd[0], PATH_MAX,
     5         kx                     "%s/chrefs --operation=dec --destination=%s %s > /dev/null 2>&1",
     5         kx                     selfdir, pkgs_path, basename( (char *)remlog_fname ) );
     5         kx   if( len == 0 || len == PATH_MAX - 1 )
     5         kx   {
     5         kx     FATAL_ERROR( "Cannot decrement '%s-%s' package references", pkgname, pkgver );
     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) && !ignore_chrefs_errors )
     5         kx   {
     5         kx     free( cmd );
     5         kx     free( tmp );
     5         kx 
     5         kx     exit_status = 48;
     5         kx 
     5         kx     if( update_mode != CONSOLE )
     5         kx     {
     5         kx #if defined( HAVE_DIALOG )
     5         kx       info_pkg_box( "Update:", pkgname, pkgver, NULL,
     5         kx                     "\n\\Z1Cannot decrement package references in Setup Database.\\Zn\n", 5, 0, 0 );
     5         kx #else
     5         kx       fprintf( stdout, "\nCannot decrement '%s-%s' package references in Setup Database.\n\n", pkgname, pkgver );
     5         kx #endif
     5         kx     }
     5         kx     else
     5         kx     {
     5         kx       fprintf( stdout, "\nCannot decrement '%s-%s' package references in Setup Database.\n\n", pkgname, pkgver );
     5         kx     }
     5         kx 
     5         kx     if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); }
     5         kx     free_resources();
     5         kx     exit( exit_status );
     5         kx   }
     5         kx 
     5         kx   /*****************************************************
     5         kx     Backup PKGLOG file into removed-packages directory:
     5         kx    */
     5         kx   bzero( (void *)tmp, PATH_MAX );
     5         kx 
     5         kx   if( installed_group )
     5         kx     (void)sprintf( &tmp[0], "%s/%s/", rempkgs_path, installed_group );
     5         kx   else
     5         kx     (void)sprintf( &tmp[0], "%s/", rempkgs_path );
     5         kx 
     5         kx   if( _mkdir_p( tmp, S_IRWXU | S_IRWXG | S_IRWXO ) != 0 )
     5         kx   {
     5         kx     FATAL_ERROR( "Cannot access '/%s' directory", REMOVED_PKGS_PATH );
     5         kx   }
     5         kx 
     5         kx   bzero( (void *)cmd, PATH_MAX );
     5         kx 
     5         kx   len = snprintf( &cmd[0], PATH_MAX,
     5         kx                   "mv %s %s > /dev/null 2>&1",
     5         kx                   remlog_fname, (const char *)&tmp[0] );
     5         kx   if( len == 0 || len == PATH_MAX - 1 )
     5         kx   {
     5         kx     FATAL_ERROR( "Cannot backup '%s' pkglog file", basename( (char *)remlog_fname ) );
     5         kx   }
     5         kx   p = sys_exec_command( cmd );
     5         kx   rc = sys_wait_command( p, (char *)NULL, PATH_MAX );
     5         kx 
     5         kx   free( cmd );
     5         kx 
     5         kx   if( rc != 0 )
     5         kx   {
     5         kx     free( tmp );
     5         kx 
     5         kx     exit_status = 47;
     5         kx 
     5         kx     if( update_mode != CONSOLE )
     5         kx     {
     5         kx #if defined( HAVE_DIALOG )
     5         kx       info_pkg_box( "Update:", pkgname, pkgver, NULL,
     5         kx                     "\n\\Z1Cannot backup PKGLOG file.\\Zn\n", 5, 0, 0 );
     5         kx #else
     5         kx       fprintf( stdout, "\nCannot backup '%s' pkglog file.\n\n", basename( (char *)remlog_fname ) );
     5         kx #endif
     5         kx     }
     5         kx     else
     5         kx     {
     5         kx       fprintf( stdout, "\nCannot backup '%s' pkglog file.\n\n", basename( (char *)remlog_fname ) );
     5         kx     }
     5         kx 
     5         kx     if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); }
     5         kx     free_resources();
     5         kx     exit( exit_status );
     5         kx   }
     5         kx 
     5         kx   /****************************************
     5         kx     Remove group directory if it is empty:
     5         kx    */
     5         kx   bzero( (void *)tmp, PATH_MAX );
     5         kx 
     5         kx   if( installed_group )
     5         kx   {
     5         kx     (void)sprintf( &tmp[0], "%s/%s/", pkgs_path, installed_group );
     5         kx 
     5         kx     const char *dir = (const char *)&tmp[0];
     5         kx     if( is_dir_empty( dir ) )
     5         kx     {
     5         kx       (void)rmdir( dir );
     5         kx     }
     5         kx   }
     5         kx 
     5         kx   free( tmp );
     5         kx }
     5         kx 
     5         kx /*
     5         kx   End of rollback/remove functions.
     5         kx  ********************************************************/
     5         kx 
     5         kx static void read_description( const char *path, int show_compressed_size )
     5         kx {
     5         kx   struct stat st;
     5         kx   FILE  *fp = NULL;
     5         kx 
     5         kx   char  *buf = NULL, *tmp = NULL;
     5         kx   char  *lp  = NULL;
     5         kx   int    n   = 0;
     5         kx 
     5         kx   char *ln   = NULL;
     5         kx   char *line = NULL;
     5         kx 
     5         kx   if( !path ) return;
     5         kx 
     5         kx   tmp = (char *)malloc( (size_t)PATH_MAX );
     5         kx   if( !tmp ) { FATAL_ERROR( "Cannot allocate memory" ); }
     5         kx   bzero( (void *)tmp, PATH_MAX );
     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)sprintf( &tmp[0], "%s/.DESCRIPTION", path );
     5         kx   bzero( (void *)&st, sizeof( struct stat ) );
     5         kx   if( (stat( (const char *)&tmp[0], &st ) == -1) || (st.st_size < 8) )
     5         kx   {
     5         kx     free( tmp );
     5         kx     return;
     5         kx   }
     5         kx 
     5         kx   fp = fopen( (const char *)&tmp[0], "r" );
     5         kx   if( !fp )
     5         kx   {
     5         kx     FATAL_ERROR( "Cannot open .DESCRIPTION file" );
     5         kx   }
     5         kx 
     5         kx   (void)sprintf( (char *)&buf[0], "%s:", pkgname );
     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   lp = (char *)&tmp[0];
     5         kx   bzero( (void *)tmp, PATH_MAX );
     5         kx   (void)sprintf( (char *)&tmp[0], "\n" );
     5         kx   ++lp;
     5         kx 
     5         kx   while( (ln = fgets( line, PATH_MAX, fp )) && 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       match += plen + 1;
     5         kx       if( match[0] != '\0' ) { (void)sprintf( lp, " %s\n", match ); lp += strlen( match ) + 2; }
     5         kx       else                   { (void)sprintf( lp, "\n" ); ++lp; }
     5         kx       ++n;
     5         kx     }
     5         kx   } /* End of while( ln = fgets() ) */
     5         kx 
     5         kx   fclose( fp );
     5         kx 
     5         kx   (void)sprintf( lp, " Uncompressed Size: %s\n", uncompressed_size );
     5         kx   lp += strlen( uncompressed_size ) + 21;
     5         kx   if( show_compressed_size )
     5         kx   {
     5         kx     (void)sprintf( lp, "   Compressed Size: %s\n", compressed_size );
     5         kx     lp += strlen( compressed_size ) + 21;
     5         kx   }
     5         kx 
     5         kx   if( description ) { free( description ); description = NULL; }
     5         kx   description = xstrdup( (const char *)&tmp[0] );
     5         kx 
     5         kx   free( buf );
     5         kx   free( line );
     5         kx   free( tmp );
     5         kx }
     5         kx 
     5         kx static int ask_for_update( void )
     5         kx {
     5         kx   int ret = 0; /* continue update */
     5         kx #if defined( HAVE_DIALOG )
     5         kx   /******************************************************
     5         kx     Ask for update dialog shown only in MENUDIALOG mode
     5         kx     when priority != REQUIRED or --always-ask=yes:
     5         kx    */
     5         kx   if( (update_mode == MENUDIALOG) && (((priority == REQUIRED) && ask) || (priority != REQUIRED)) )
     5         kx   {
     5         kx     ret =  ask_update_box( "Update:", pkgname, pkgver, strprio( priority, 0 ),
     5         kx                            description, 18, 0, 0 );
     5         kx   }
     5         kx 
     5         kx   if( ret )
     5         kx   {
     5         kx     info_pkg_box( "Update:", pkgname, pkgver, strprio( priority, 0 ),
     5         kx                   "\nUpdate terminated by user.\n", 5, 0, 0 );
     5         kx   }
     5         kx #endif
     5         kx   return ret;
     5         kx }
     5         kx 
     5         kx static int ask_for_reinstall( void )
     5         kx {
     5         kx   int ret = 0; /* continue update */
     5         kx #if defined( HAVE_DIALOG )
     5         kx   /***************************************************************
     5         kx     Ask for remove dialog shown only in INFO or MENU DIALOG mode:
     5         kx    */
     5         kx   if( update_mode == MENUDIALOG )
     5         kx   {
     5         kx     char *msg = NULL;
     5         kx 
     5         kx     msg = (char *)malloc( (size_t)PATH_MAX );
     5         kx     if( !msg ) { FATAL_ERROR( "Cannot allocate memory" ); }
     5         kx     bzero( (void *)msg, PATH_MAX );
     5         kx 
     5         kx     (void)sprintf( &msg[0], "\nThe same version of requested package installed and correct.\n"
     5         kx                             "\n\\Z1Re-install this vesion?\\Zn\n" );
     5         kx 
     5         kx     if( (ret =  ask_reinstall_box( "Update:", pkgname, installed_version, (const char *)&msg[0], 9, 0, 0 )) )
     5         kx     {
     5         kx       info_pkg_box( "Update:", pkgname, installed_version, NULL,
     5         kx                     "\nPackage update terminated by user.\n", 5, 0, 0 );
     5         kx     }
     5         kx 
     5         kx     free( msg );
     5         kx   }
     5         kx   else if( update_mode == INFODIALOG )
     5         kx   {
     5         kx     if( !reinstall )
     5         kx     {
     5         kx       info_pkg_box( "Update:", pkgname, installed_version, NULL,
     5         kx                     "\nThe same version of requested package installed and correct.\n", 5, 0, 0 );
     5         kx       ret = 1;
     5         kx     }
     5         kx   }
     5         kx   else
     5         kx   {
     5         kx     if( !reinstall )
     5         kx     {
     5         kx       fprintf( stdout, "\nThe same version of '%s-%s' package is installed and correct.\n\n", pkgname, installed_version );
     5         kx       ret = 1;
     5         kx     }
     5         kx   }
     5         kx #else
     5         kx   {
     5         kx     if( !reinstall )
     5         kx     {
     5         kx       fprintf( stdout, "\nThe same version of '%s-%s' package is installed and correct.\n\n", pkgname, installed_version );
     5         kx       ret = 1;
     5         kx     }
     5         kx   }
     5         kx #endif
     5         kx   return ret;
     5         kx }
     5         kx 
     5         kx 
     5         kx static void show_update_progress( void )
     5         kx {
     5         kx   fprintf( stdout, "\033[2J" ); /* clear screen */
     5         kx 
     5         kx   if( update_mode != CONSOLE )
     5         kx   {
     5         kx #if defined( HAVE_DIALOG )
     5         kx     info_pkg_box( "Update:", pkgname, pkgver, strprio( priority, 0 ),
     5         kx                   description, 16, 0, 0 );
     5         kx #else
     5         kx     fprintf( stdout, "\n Update: %s-%s [%s]...\n", pkgname, pkgver, strprio( priority, 0 ));
     5         kx     /*************************************************
     5         kx       Ruler: 68 characters + 2 spaces left and right:
     5         kx 
     5         kx                       | ----handy-ruler----------------------------------------------------- | */
     5         kx     fprintf( stdout, "|======================================================================|\n" );
     5         kx     fprintf( stdout, "%s\n", description );
     5         kx     fprintf( stdout, "|======================================================================|\n\n" );
     5         kx     fprintf( stdout, "\n\n\n" ); /* 3 lines up for final message */
     5         kx #endif
     5         kx   }
     5         kx   else
     5         kx   {
     5         kx     fprintf( stdout, "\n Update: %s-%s [%s]...\n", pkgname, pkgver, strprio( priority, 0 ));
     5         kx     /*************************************************
     5         kx       Ruler: 68 characters + 2 spaces left and right:
     5         kx 
     5         kx                       | ----handy-ruler----------------------------------------------------- | */
     5         kx     fprintf( stdout, "|======================================================================|\n" );
     5         kx     fprintf( stdout, "%s\n", description );
     5         kx     fprintf( stdout, "|======================================================================|\n\n" );
     5         kx     fprintf( stdout, "\n\n\n" ); /* 3 lines up for final message */
     5         kx   }
     5         kx }
     5         kx 
     5         kx 
     5         kx static void pre_update_routine( void )
     5         kx {
     5         kx   pid_t p = (pid_t) -1;
     5         kx   int   rc;
     5         kx 
     5         kx   int   len = 0;
     5         kx   char *cmd = NULL;
     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,
     5         kx                   "cd %s && %s/.INSTALL pre_update %s %s > /dev/null 2>&1",
     5         kx                   root, tmpdir, pkgver, installed_version );
     5         kx   if( len == 0 || len == PATH_MAX - 1 )
     5         kx   {
     5         kx     FATAL_ERROR( "Cannot run pre-update script for '%s-%s' package", pkgname, pkgver );
     5         kx   }
     5         kx   p = sys_exec_command( cmd );
     5         kx   rc = sys_wait_command( p, (char *)NULL, PATH_MAX );
     5         kx 
     5         kx   free( cmd );
     5         kx 
     5         kx   if( rc != 0 )
     5         kx   {
     5         kx     exit_status = 43;
     5         kx 
     5         kx     if( update_mode != CONSOLE )
     5         kx     {
     5         kx #if defined( HAVE_DIALOG )
     5         kx       info_pkg_box( "Update:", pkgname, pkgver, strprio( priority, 0 ),
     5         kx                     "\n\\Z1Pre-update script returned error status.\\Zn\n", 5, 0, 0 );
     5         kx #else
     5         kx       fprintf( stdout, "\nPre-update script of '%s-%s' returned error status.\n\n", pkgname, pkgver );
     5         kx #endif
     5         kx     }
     5         kx     else
     5         kx     {
     5         kx       fprintf( stdout, "\nPre-update script of '%s-%s' returned error status.\n\n", pkgname, pkgver );
     5         kx     }
     5         kx 
     5         kx     if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); }
     5         kx     free_resources();
     5         kx     exit( exit_status );
     5         kx   }
     5         kx }
     5         kx 
     5         kx static const char *fill_decompressor( char *buffer, char compressor )
     5         kx {
     5         kx   switch( compressor )
     5         kx   {
     5         kx     case 'J':
     5         kx       (void)sprintf( buffer, "xz --threads=%d -dc", get_nprocs() );
     5         kx       break;
     5         kx     case 'j':
     5         kx       (void)sprintf( buffer, "bzip2 -dc" );
     5         kx       break;
     5         kx     case 'z':
     5         kx       (void)sprintf( buffer, "gzip -dc" );
     5         kx       break;
     5         kx     default:
     5         kx       (void)sprintf( buffer, "cat -" );
     5         kx       break;
     5         kx   }
     5         kx   return (const char *)buffer;
     5         kx }
     5         kx 
     5         kx static void uncompress_package( void )
     5         kx {
     5         kx   pid_t p = (pid_t) -1;
     5         kx   int   rc;
     5         kx 
     5         kx   int   len = 0;
     5         kx   char *cmd = NULL;
     5         kx 
     5         kx   char decompressor[64];
     5         kx 
     5         kx   (void)fill_decompressor( (char *)&decompressor[0], uncompress );
     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,
     5         kx                   "cat %s | %s  | tar -C %s "
     5         kx                   "--exclude='.DESCRIPTION' "
     5         kx                   "--exclude='.FILELIST' "
     5         kx                   "--exclude='.INSTALL' "
     5         kx                   "--exclude='.PKGINFO' "
     5         kx                   "--exclude='.REQUIRES' "
     5         kx                   "--exclude='.RESTORELINKS' "
     5         kx                   "-xf - > /dev/null 2>&1",
     5         kx                   pkg_fname, decompressor, root );
     5         kx   if( len == 0 || len == PATH_MAX - 1 )
     5         kx   {
     5         kx     FATAL_ERROR( "Cannot uncompress '%s-%s' package", pkgname, pkgver );
     5         kx   }
     5         kx   p = sys_exec_command( cmd );
     5         kx   rc = sys_wait_command( p, (char *)NULL, PATH_MAX );
     5         kx 
     5         kx   free( cmd );
     5         kx 
     5         kx   if( rc != 0 )
     5         kx   {
     5         kx     exit_status = 44;
     5         kx 
     5         kx     if( update_mode != CONSOLE )
     5         kx     {
     5         kx #if defined( HAVE_DIALOG )
     5         kx       info_pkg_box( "Update:", pkgname, pkgver, strprio( priority, 0 ),
     5         kx                     "\n\\Z1Cannot uncompress package.\\Zn\n", 5, 0, 0 );
     5         kx #else
     5         kx       fprintf( stdout, "\nCannot uncompress '%s-%s' package.\n\n", pkgname, pkgver );
     5         kx #endif
     5         kx     }
     5         kx     else
     5         kx     {
     5         kx       fprintf( stdout, "\nCannot uncompress '%s-%s' package.\n\n", pkgname, pkgver );
     5         kx     }
     5         kx 
     5         kx     if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); }
     5         kx     free_resources();
     5         kx     exit( exit_status );
     5         kx   }
     5         kx }
     5         kx 
     5         kx static void restore_links( void )
     5         kx {
     5         kx   struct stat st;
     5         kx   pid_t  p = (pid_t) -1;
     5         kx   int    rc;
     5         kx 
     5         kx   int   len = 0;
     5         kx   char *cmd = NULL;
     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   (void)sprintf( &cmd[0], "%s/.RESTORELINKS", tmpdir );
     5         kx   bzero( (void *)&st, sizeof( struct stat ) );
     5         kx   if( (stat( (const char *)&cmd[0], &st ) == -1) || (st.st_size < 8) )
     5         kx   {
     5         kx     free( cmd );
     5         kx     return;
     5         kx   }
     5         kx   bzero( (void *)cmd, PATH_MAX );
     5         kx 
     5         kx   len = snprintf( &cmd[0], PATH_MAX,
     5         kx                   "cd %s && sh %s/.RESTORELINKS > /dev/null 2>&1",
     5         kx                   root, tmpdir );
     5         kx   if( len == 0 || len == PATH_MAX - 1 )
     5         kx   {
     5         kx     FATAL_ERROR( "Cannot restore links for '%s-%s' package", pkgname, pkgver );
     5         kx   }
     5         kx   p = sys_exec_command( cmd );
     5         kx   rc = sys_wait_command( p, (char *)NULL, PATH_MAX );
     5         kx 
     5         kx   free( cmd );
     5         kx 
     5         kx   if( rc != 0 )
     5         kx   {
     5         kx     rollback();
     5         kx 
     5         kx     exit_status = 45;
     5         kx 
     5         kx     if( update_mode != CONSOLE )
     5         kx     {
     5         kx #if defined( HAVE_DIALOG )
     5         kx       info_pkg_box( "Update:", pkgname, pkgver, strprio( priority, 0 ),
     5         kx                     "\n\\Z1Restore-links script returned error status.\\Zn\n", 5, 0, 0 );
     5         kx #else
     5         kx       fprintf( stdout, "\nRestore-links script of '%s-%s' returned error status.\n\n", pkgname, pkgver );
     5         kx #endif
     5         kx     }
     5         kx     else
     5         kx     {
     5         kx       fprintf( stdout, "\nRestore-links script of '%s-%s' returned error status.\n\n", pkgname, pkgver );
     5         kx     }
     5         kx 
     5         kx     if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); }
     5         kx     free_resources();
     5         kx     exit( exit_status );
     5         kx   }
     5         kx }
     5         kx 
     5         kx static void post_update_routine( void )
     5         kx {
     5         kx   pid_t p = (pid_t) -1;
     5         kx   int   rc;
     5         kx 
     5         kx   int   len = 0;
     5         kx   char *cmd = NULL;
     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,
     5         kx                   "cd %s && %s/.INSTALL post_update %s %s > /dev/null 2>&1",
     5         kx                   root, tmpdir, pkgver, installed_version );
     5         kx   if( len == 0 || len == PATH_MAX - 1 )
     5         kx   {
     5         kx     FATAL_ERROR( "Cannot run post-update script for '%s-%s' package", pkgname, pkgver );
     5         kx   }
     5         kx   p = sys_exec_command( cmd );
     5         kx   rc = sys_wait_command( p, (char *)NULL, PATH_MAX );
     5         kx 
     5         kx   free( cmd );
     5         kx 
     5         kx   if( rc != 0 )
     5         kx   {
     5         kx     rollback();
     5         kx 
     5         kx     exit_status = 46;
     5         kx 
     5         kx     if( update_mode != CONSOLE )
     5         kx     {
     5         kx #if defined( HAVE_DIALOG )
     5         kx       info_pkg_box( "Update:", pkgname, pkgver, strprio( priority, 0 ),
     5         kx                     "\n\\Z1Post-update script returned error status.\\Zn\n", 5, 0, 0 );
     5         kx #else
     5         kx       fprintf( stdout, "\nPost-update script of '%s-%s' returned error status.\n\n", pkgname, pkgver );
     5         kx #endif
     5         kx     }
     5         kx     else
     5         kx     {
     5         kx       fprintf( stdout, "\nPost-update script of '%s-%s' returned error status.\n\n", pkgname, pkgver );
     5         kx     }
     5         kx 
     5         kx     if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); }
     5         kx     free_resources();
     5         kx     exit( exit_status );
     5         kx   }
     5         kx }
     5         kx 
     5         kx static void finalize_update( void )
     5         kx {
     5         kx   pid_t p = (pid_t) -1;
     5         kx   int   rc;
     5         kx 
     5         kx   int   len = 0;
     5         kx   char *cmd = NULL, *tmp = NULL;
     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   tmp = (char *)malloc( (size_t)PATH_MAX );
     5         kx   if( !tmp ) { FATAL_ERROR( "Cannot allocate memory" ); }
     5         kx   bzero( (void *)tmp, PATH_MAX );
     5         kx 
     5         kx   if( group )
     5         kx     (void)sprintf( &tmp[0], "%s/%s/", pkgs_path, group );
     5         kx   else
     5         kx     (void)sprintf( &tmp[0], "%s/", pkgs_path );
     5         kx 
     5         kx   if( _mkdir_p( tmp, S_IRWXU | S_IRWXG | S_IRWXO ) != 0 )
     5         kx   {
     5         kx     FATAL_ERROR( "Cannot access '/%s' directory", PACKAGES_PATH );
     5         kx   }
     5         kx 
     5         kx   /****************************************
     5         kx     Store PKGLOG file into Setup Database:
     5         kx    */
     5         kx   len = snprintf( &cmd[0], PATH_MAX,
     5         kx                   "cp %s %s > /dev/null 2>&1",
     5         kx                   pkglog_fname, (const char *)&tmp[0] );
     5         kx   if( len == 0 || len == PATH_MAX - 1 )
     5         kx   {
     5         kx     FATAL_ERROR( "Cannot store '%s' pkglog file", basename( (char *)pkglog_fname ) );
     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     rollback();
     5         kx 
     5         kx     free( cmd );
     5         kx     free( tmp );
     5         kx 
     5         kx     exit_status = 47;
     5         kx 
     5         kx     if( update_mode != CONSOLE )
     5         kx     {
     5         kx #if defined( HAVE_DIALOG )
     5         kx       info_pkg_box( "Update:", pkgname, pkgver, strprio( priority, 0 ),
     5         kx                     "\n\\Z1Cannot store PKGLOG file into Setup Database.\\Zn\n", 5, 0, 0 );
     5         kx #else
     5         kx       fprintf( stdout, "\nCannot store '%s' pkglog file into Setup Database.\n\n", basename( (char *)pkglog_fname ) );
     5         kx #endif
     5         kx     }
     5         kx     else
     5         kx     {
     5         kx       fprintf( stdout, "\nCannot store '%s' pkglog file into Setup Database.\n\n", basename( (char *)pkglog_fname ) );
     5         kx     }
     5         kx 
     5         kx     if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); }
     5         kx     free_resources();
     5         kx     exit( exit_status );
     5         kx   }
     5         kx 
     5         kx   /*********************************************
     5         kx     Increment references in the Setup Database:
     5         kx    */
     5         kx   if( group )
     5         kx     len = snprintf( &cmd[0], PATH_MAX,
     5         kx                     "%s/chrefs --operation=inc --destination=%s %s/%s > /dev/null 2>&1",
     5         kx                     selfdir, pkgs_path, group, basename( (char *)pkglog_fname ) );
     5         kx   else
     5         kx     len = snprintf( &cmd[0], PATH_MAX,
     5         kx                     "%s/chrefs --operation=inc --destination=%s %s > /dev/null 2>&1",
     5         kx                     selfdir, pkgs_path, basename( (char *)pkglog_fname ) );
     5         kx   if( len == 0 || len == PATH_MAX - 1 )
     5         kx   {
     5         kx     FATAL_ERROR( "Cannot increment '%s-%s' package references", pkgname, pkgver );
     5         kx   }
     5         kx   p = sys_exec_command( cmd );
     5         kx   rc = sys_wait_command( p, (char *)NULL, PATH_MAX );
     5         kx 
     5         kx   free( cmd );
     5         kx 
     5         kx   if( (rc != 0) && !ignore_chrefs_errors )
     5         kx   {
     5         kx     free( tmp );
     5         kx 
     5         kx     rollback();
     5         kx 
     5         kx     exit_status = 48;
     5         kx 
     5         kx     if( update_mode != CONSOLE )
     5         kx     {
     5         kx #if defined( HAVE_DIALOG )
     5         kx       info_pkg_box( "Update:", pkgname, pkgver, strprio( priority, 0 ),
     5         kx                     "\n\\Z1Cannot increment package references in Setup Database.\\Zn\n", 5, 0, 0 );
     5         kx #else
     5         kx       fprintf( stdout, "\nCannot increment '%s-%s' package references in Setup Database.\n\n", pkgname, pkgver );
     5         kx #endif
     5         kx     }
     5         kx     else
     5         kx     {
     5         kx       fprintf( stdout, "\nCannot increment '%s-%s' package references in Setup Database.\n\n", pkgname, pkgver );
     5         kx     }
     5         kx 
     5         kx     if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); }
     5         kx     free_resources();
     5         kx     exit( exit_status );
     5         kx   }
     5         kx 
     5         kx   /*************************************************
     5         kx     Remove backup PKGLOG file from removed-packages
     5         kx     directory if exists:
     5         kx    */
     5         kx   bzero( (void *)tmp, PATH_MAX );
     5         kx   {
     5         kx     const char *fname = basename( (char *)pkglog_fname );
     5         kx 
     5         kx     if( group )
     5         kx       (void)sprintf( &tmp[0], "%s/%s/%s", rempkgs_path, group, fname );
     5         kx     else
     5         kx       (void)sprintf( &tmp[0], "%s/%s", rempkgs_path, fname );
     5         kx 
     5         kx     (void)unlink( (const char *)&tmp[0] );
     5         kx 
     5         kx     if( group )
     5         kx     {
     5         kx       const char *dir = (const char *)dirname( (char *)&tmp[0] );
     5         kx       if( is_dir_empty( dir ) )
     5         kx       {
     5         kx         (void)rmdir( dir );
     5         kx       }
     5         kx     }
     5         kx   }
     5         kx 
     5         kx   free( tmp );
     5         kx }
     5         kx 
     5         kx #if defined( HAVE_GPG2 )
     5         kx static void verify_gpg_signature( void )
     5         kx {
     5         kx   struct stat st;
     5         kx   pid_t  p = (pid_t) -1;
     5         kx   int    rc;
     5         kx 
     5         kx   int   len = 0;
     5         kx   char *cmd = NULL;
     5         kx 
     5         kx   bzero( (void *)&st, sizeof( struct stat ) );
     5         kx 
     5         kx   /******************************************************************
     5         kx     Do not try to verify signature if '.asc' file is not accessible:
     5         kx    */
     5         kx   if( stat( (const char *)asc_fname, &st ) == -1 ) return;
     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,
     5         kx                   "gpg2 --verify %s %s > /dev/null 2>&1",
     5         kx                   asc_fname, pkg_fname );
     5         kx   if( len == 0 || len == PATH_MAX - 1 )
     5         kx   {
     5         kx     FATAL_ERROR( "Cannot verify GPG2 signature of '%s-%s' package", pkgname, pkgver );
     5         kx   }
     5         kx   p = sys_exec_command( cmd );
     5         kx   rc = sys_wait_command( p, (char *)NULL, PATH_MAX );
     5         kx 
     5         kx   free( cmd );
     5         kx 
     5         kx   if( rc != 0 )
     5         kx   {
     5         kx     exit_status = 51;
     5         kx 
     5         kx     if( update_mode != CONSOLE )
     5         kx     {
     5         kx #if defined( HAVE_DIALOG )
     5         kx       info_pkg_box( "Update:", pkgname, pkgver, strprio( priority, 0 ),
     5         kx                     "\n\\Z1Cannot verify GPG2 signature of the package.\\Zn\n", 5, 0, 0 );
     5         kx #else
     5         kx       fprintf( stdout, "\nGPG2 signature verification of '%s-%s' package returned error status.\n\n", pkgname, pkgver );
     5         kx #endif
     5         kx     }
     5         kx     else
     5         kx     {
     5         kx       fprintf( stdout, "\nGPG2 signature verification of '%s-%s' package returned error status.\n\n", pkgname, pkgver );
     5         kx     }
     5         kx 
     5         kx     if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); }
     5         kx     free_resources();
     5         kx     exit( exit_status );
     5         kx   }
     5         kx }
     5         kx #endif
     5         kx 
     5         kx 
     5         kx static void dialogrc( void )
     5         kx {
     5         kx   struct stat st;
     5         kx   char  *tmp = NULL;
     5         kx 
     5         kx   tmp = (char *)malloc( (size_t)PATH_MAX );
     5         kx   if( !tmp ) { FATAL_ERROR( "Cannot allocate memory" ); }
     5         kx   bzero( (void *)tmp, PATH_MAX );
     5         kx 
     5         kx   /* imagine that the utility is in /sbin directory: */
     5         kx   (void)sprintf( &tmp[0], "%s/../usr/share/%s/.dialogrc", selfdir, PACKAGE_NAME );
     5         kx   if( stat( (const char *)&tmp[0], &st ) == -1 )
     5         kx   {
     5         kx     /* finaly assume that /usr/sbin is a sbindir: */
     5         kx     (void)sprintf( &tmp[0], "%s/../../usr/share/%s/.dialogrc", selfdir, PACKAGE_NAME );
     5         kx   }
     5         kx 
     5         kx   setenv( "DIALOGRC", (const char *)&tmp[0], 1 );
     5         kx 
     5         kx   free( tmp );
     5         kx }
     5         kx 
     5         kx static char *get_curdir( void )
     5         kx {
     5         kx   char *cwd = NULL;
     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   if( getcwd( cwd, (size_t)PATH_MAX ) != NULL )
     5         kx   {
     5         kx     char *p = NULL;
     5         kx     remove_trailing_slash( cwd );
     5         kx     p = xstrdup( (const char *)cwd );
     5         kx     free( cwd );
     5         kx     return p;
     5         kx   }
     5         kx   else
     5         kx   {
     5         kx     FATAL_ERROR( "Cannot get absolute path to current directory" );
     5         kx   }
     5         kx 
     5         kx   return (char *)NULL;
     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( 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   curdir  = get_curdir();
     5         kx   dialogrc();
     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 
     5         kx   {
     5         kx     int status = 0;
     5         kx 
     5         kx     /**********************************************************
     5         kx       Fill pkginfo data and put or replace pkglog into tmpdir:
     5         kx      */
     5         kx     status = check_installed_package();
     5         kx 
     5         kx     read_filelist( (void *)&rdirs, (void *)&rfiles, (const char *)rtmpdir );
     5         kx     read_restorelinks( (void *)&rlinks, (const char *)rtmpdir );
     5         kx     read_description( (const char *)rtmpdir, 0 );
     5         kx     if( status == 0 )
     5         kx     {
     5         kx       int integrity = check_installed_pkg_integrity();
     5         kx       if( integrity ) /* not depends on --always-ask option */
     5         kx       {
     5         kx         /* same version of requested package is already installed and correct: */
     5         kx         if( ask_for_reinstall() )
     5         kx         {
     5         kx           /* Terminate update procedure with success return code: */
     5         kx           if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); }
     5         kx           free_resources();
     5         kx           exit( exit_status );
     5         kx         }
     5         kx       }
     5         kx     }
     5         kx   }
     5         kx 
     5         kx   /************************************************************
     5         kx     Getting Service Files, reading pkginfo, preserving pkglog:
     5         kx    */
     5         kx   read_service_files();
     5         kx 
     5         kx   if( rqck ) check_requires();
     5         kx #if defined( HAVE_GPG2 )
     5         kx   if( gpgck ) verify_gpg_signature();
     5         kx #endif
     5         kx 
     5         kx   read_filelist( (void *)&dirs, (void *)&files, (const char *)tmpdir );
     5         kx   read_restorelinks( (void *)&links, (const char *)tmpdir );
     5         kx 
     5         kx   read_description( (const char *)tmpdir, 1 );
     5         kx 
     5         kx   if( ask_for_update() )
     5         kx   {
     5         kx     /* Terminate update: */
     5         kx     if( tmpdir ) { _rm_tmpdir( (const char *)tmpdir ); free( tmpdir ); }
     5         kx     free_resources();
     5         kx     exit( exit_status );
     5         kx   }
     5         kx 
     5         kx   /************
     5         kx     DO REMOVE:
     5         kx    */
     5         kx   remove_package();
     5         kx   finalize_removal();
     5         kx 
     5         kx   show_update_progress();
     5         kx 
     5         kx   /************
     5         kx     DO UPDATE:
     5         kx    */
     5         kx   pre_update_routine();
     5         kx   uncompress_package();
     5         kx   restore_links();
     5         kx   post_update_routine();
     5         kx   finalize_update();
     5         kx 
     5         kx   fprintf( stdout, "\033[3A" ); /* move cursor up 3 lines */
     5         kx 
     5         kx   if( (update_mode != CONSOLE) && (update_mode == MENUDIALOG) )
     5         kx   {
     5         kx #if defined( HAVE_DIALOG )
     5         kx     info_pkg_box( "Update:", pkgname, pkgver, strprio( priority, 0 ),
     5         kx                   "\nPackage has been updated.\n", 5, 0, 0 );
     5         kx #else
     5         kx     fprintf( stdout, "\nPackage '%s-%s' has been updated.\n\n", pkgname, pkgver );
     5         kx #endif
     5         kx   }
     5         kx   else
     5         kx   {
     5         kx     if( (update_mode != INFODIALOG) )
     5         kx     {
     5         kx       fprintf( stdout, "\nPackage '%s-%s' has been updated.\n\n", pkgname, pkgver );
     5         kx     }
     5         kx   }
     5         kx 
     5         kx   setup_log( "Package '%s-%s' has been updated", pkgname, pkgver );
     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 }