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/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 <sys/resource.h>
     5         kx 
     5         kx #include <signal.h>
     5         kx #if !defined SIGCHLD && defined SIGCLD
     5         kx # define SIGCHLD SIGCLD
     5         kx #endif
     5         kx 
     5         kx #define _GNU_SOURCE
     5         kx #include <getopt.h>
     5         kx 
     5         kx #include <msglog.h>
     5         kx #include <wrapper.h>
     5         kx #include <system.h>
     5         kx 
     5         kx #define PROGRAM_NAME "chrefs"
     5         kx 
     5         kx #include <defs.h>
     5         kx 
     5         kx 
     5         kx char *program     = PROGRAM_NAME;
     5         kx char *destination = NULL, *operation = NULL, *pkglog_fname = NULL,
     5         kx      *tmpdir = NULL, *requires_fname = NULL;
     5         kx 
     5         kx int   exit_status = EXIT_SUCCESS; /* errors counter */
     5         kx char *selfdir     = NULL;
     5         kx 
     5         kx 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 
     5         kx 
     5         kx /********************************************
     5         kx   *requires[] declarations:
     5         kx  */
     5         kx struct package {
     5         kx   char *name;
     5         kx   char *version;
     5         kx   char *arch;
     5         kx   char *distro_name;
     5         kx   char *distro_version;
     5         kx };
     5         kx 
     5         kx static struct package *create_package( char *name, char *version,
     5         kx                                        char *arch, char *distro_name,
     5         kx                                        char *distro_version );
     5         kx static void free_package( struct package *pkg );
     5         kx static struct package **create_requires( size_t size );
     5         kx static void free_requires( struct package **requires );
     5         kx /*
     5         kx   End of *requires[] declarations.
     5         kx  ********************************************/
     5         kx 
     5         kx struct package **requires = NULL;
     5         kx 
     5         kx 
     5         kx /********************************************
     5         kx   LOCK FILE declarations:
     5         kx  */
     5         kx static int __lock_file( FILE *fp );
     5         kx static void __unlock_file( int fd );
     5         kx /*
     5         kx   End of LOCK FILE declarations.
     5         kx  ********************************************/
     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( requires )          { free_requires( requires ); } requires   = NULL
     5         kx 
     5         kx void free_resources()
     5         kx {
     5         kx   if( selfdir )        { free( selfdir );        selfdir        = NULL; }
     5         kx   if( destination )    { free( destination );    destination    = NULL; }
     5         kx   if( operation )      { free( operation );      operation      = NULL; }
     5         kx   if( pkglog_fname )   { free( pkglog_fname );   pkglog_fname   = NULL; }
     5         kx   if( requires_fname ) { free( requires_fname ); requires_fname = 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] <pkglog>\n", program );
     5         kx   fprintf( stdout, "\n" );
     5         kx   fprintf( stdout, "Increment or Decrement reference counters in required PKGLOG files\n" );
     5         kx   fprintf( stdout, "in the  Setup Database packages  directory, which is determined by\n" );
     5         kx   fprintf( stdout, "option --destination  OR by the directory where the input <pkglog>\n" );
     5         kx   fprintf( stdout, "file is located. If destination is defined then <pkglog> file name\n" );
     5         kx   fprintf( stdout, "should be defined relative to destination.\n" );
     5         kx   fprintf( stdout, "\n" );
     5         kx   fprintf( stdout, "Options:\n" );
     5         kx   fprintf( stdout, "  -h,--help                     Display this information.\n" );
     5         kx   fprintf( stdout, "  -v,--version                  Display the version of %s utility.\n", program );
     5         kx   fprintf( stdout, "  -d,--destination=<DIR>        Setup Database packages directory.\n" );
     5         kx   fprintf( stdout, "  -o,--operation=<inc|dec>      Operation: Increment or Decrement.\n" );
     5         kx   fprintf( stdout, "\n" );
     5         kx   fprintf( stdout, "Parameter:\n" );
     5         kx   fprintf( stdout, "  <pkglog>                      Input PKGLOG file (TEXT format).\n"  );
     5         kx   fprintf( stdout, "\n" );
     5         kx 
     5         kx   exit( EXIT_FAILURE );
     5         kx }
     5         kx 
     5         kx void to_lowercase( char *s )
     5         kx {
     5         kx   char *p = s;
     5         kx   while( p && *p ) { int c = *p; *p = tolower( c ); ++p; }
     5         kx }
     5         kx 
     5         kx void to_uppercase( char *s )
     5         kx {
     5         kx   char *p = s;
     5         kx   while( p && *p ) { int c = *p; *p = toupper( c ); ++p; }
     5         kx }
     5         kx 
     5         kx void version()
     5         kx {
     5         kx   char *upper = NULL;
     5         kx 
     5         kx   upper = (char *)alloca( strlen( program ) + 1 );
     5         kx 
     5         kx   strcpy( (char *)upper, (const char *)program );
     5         kx   to_uppercase( upper );
     5         kx 
     5         kx   fprintf( stdout, "%s (%s) %s\n", program, upper, PROGRAM_VERSION );
     5         kx 
     5         kx   fprintf( stdout, "Copyright (C) 2019 Andrey V.Kosteltsev.\n" );
     5         kx   fprintf( stdout, "This is free software.   There is NO warranty; not even\n" );
     5         kx   fprintf( stdout, "for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n" );
     5         kx   fprintf( stdout, "\n" );
     5         kx 
     5         kx   free_resources();
     5         kx   exit( EXIT_SUCCESS );
     5         kx }
     5         kx 
     5         kx 
     5         kx static void remove_trailing_slash( char *dir )
     5         kx {
     5         kx   char *s;
     5         kx 
     5         kx   if( !dir || dir[0] == '\0' ) return;
     5         kx 
     5         kx   s = dir + strlen( dir ) - 1;
     5         kx   while( *s == '/' )
     5         kx   {
     5         kx     *s = '\0'; --s;
     5         kx   }
     5         kx }
     5         kx 
     5         kx 
     5         kx static int _mkdir_p( const char *dir, const mode_t mode )
     5         kx {
     5         kx   char  *buf;
     5         kx   char  *p = NULL;
     5         kx   struct stat sb;
     5         kx 
     5         kx   if( !dir ) return -1;
     5         kx 
     5         kx   buf = (char *)alloca( strlen( dir ) + 1 );
     5         kx   strcpy( buf, dir );
     5         kx 
     5         kx   remove_trailing_slash( buf );
     5         kx 
     5         kx   /* check if path exists and is a directory */
     5         kx   if( stat( buf, &sb ) == 0 )
     5         kx   {
     5         kx     if( S_ISDIR(sb.st_mode) )
     5         kx     {
     5         kx       return 0;
     5         kx     }
     5         kx   }
     5         kx 
     5         kx   /* mkdir -p */
     5         kx   for( p = buf + 1; *p; ++p )
     5         kx   {
     5         kx     if( *p == '/' )
     5         kx     {
     5         kx       *p = 0;
     5         kx       /* test path */
     5         kx       if( stat( buf, &sb ) != 0 )
     5         kx       {
     5         kx         /* path does not exist - create directory */
     5         kx         if( mkdir( buf, mode ) < 0 )
     5         kx         {
     5         kx           return -1;
     5         kx         }
     5         kx       } else if( !S_ISDIR(sb.st_mode) )
     5         kx       {
     5         kx         /* not a directory */
     5         kx         return -1;
     5         kx       }
     5         kx       *p = '/';
     5         kx     }
     5         kx   }
     5         kx 
     5         kx   /* test path */
     5         kx   if( stat( buf, &sb ) != 0 )
     5         kx   {
     5         kx     /* path does not exist - create directory */
     5         kx     if( mkdir( buf, mode ) < 0 )
     5         kx     {
     5         kx       return -1;
     5         kx     }
     5         kx   } else if( !S_ISDIR(sb.st_mode) )
     5         kx   {
     5         kx     /* not a directory */
     5         kx     return -1;
     5         kx   }
     5         kx 
     5         kx   return 0;
     5         kx }
     5         kx 
     5         kx 
     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 
     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 /********************************************
     5         kx   *requires[] functions:
     5         kx  */
     5         kx static struct package *create_package( char *name, char *version,
     5         kx                                        char *arch, char *distro_name,
     5         kx                                        char *distro_version )
     5         kx {
     5         kx   struct package *pkg = (struct package *)0;
     5         kx 
     5         kx   if( !name           || *name           == '\0' ) return pkg;
     5         kx   if( !version        || *version        == '\0' ) return pkg;
     5         kx   if( !arch           || *arch           == '\0' ) return pkg;
     5         kx   if( !distro_name    || *distro_name    == '\0' ) return pkg;
     5         kx   if( !distro_version || *distro_version == '\0' ) return pkg;
     5         kx 
     5         kx   pkg = (struct package *)malloc( sizeof(struct package) );
     5         kx   if( pkg )
     5         kx   {
     5         kx     pkg->name           = xstrdup( (const char *)name           );
     5         kx     pkg->version        = xstrdup( (const char *)version        );
     5         kx     pkg->arch           = xstrdup( (const char *)arch           );
     5         kx     pkg->distro_name    = xstrdup( (const char *)distro_name    );
     5         kx     pkg->distro_version = xstrdup( (const char *)distro_version );
     5         kx   }
     5         kx 
     5         kx   return pkg;
     5         kx }
     5         kx 
     5         kx static void free_package( struct package *pkg )
     5         kx {
     5         kx   if( pkg )
     5         kx   {
     5         kx     if( pkg->name           ) free( pkg->name           );
     5         kx     if( pkg->version        ) free( pkg->version        );
     5         kx     if( pkg->arch           ) free( pkg->arch           );
     5         kx     if( pkg->distro_name    ) free( pkg->distro_name    );
     5         kx     if( pkg->distro_version ) free( pkg->distro_version );
     5         kx 
     5         kx     free( pkg );
     5         kx   }
     5         kx }
     5         kx 
     5         kx static struct package **create_requires( size_t size )
     5         kx {
     5         kx   struct package **requires = (struct package **)0;
     5         kx 
     5         kx   if( size > 0 )
     5         kx   {
     5         kx     requires = (struct package **)malloc( size * sizeof(struct package *) );
     5         kx     bzero( (void *)requires, size * sizeof(struct package *) );
     5         kx   }
     5         kx 
     5         kx   return( requires );
     5         kx }
     5         kx 
     5         kx static void free_requires( struct package **requires )
     5         kx {
     5         kx   if( requires )
     5         kx   {
     5         kx     struct package **ptr = requires;
     5         kx 
     5         kx     while( *ptr )
     5         kx     {
     5         kx       if( *ptr ) free_package( *ptr );
     5         kx       ptr++;
     5         kx     }
     5         kx     free( requires );
     5         kx   }
     5         kx }
     5         kx /*
     5         kx   End of *requires[] functions.
     5         kx  ********************************************/
     5         kx 
     5         kx 
     5         kx /********************************************
     5         kx   LOCK FILE functions:
     5         kx  */
     5         kx static int __lock_file( FILE *fp )
     5         kx {
     5         kx   int fd = fileno( fp );
     5         kx 
     5         kx   if( flock( fd, LOCK_EX ) == -1 )
     5         kx   {
     5         kx     return -1;
     5         kx     /*
     5         kx       Мы не проверяем errno == EWOULDBLOCK, так какданная ошибка
     5         kx       говорит о том что файл заблокирован другим процессом с флагом
     5         kx       LOCK_NB, а мы не собираемся циклически проверять блокировку.
     5         kx       У нас все просто: процесс просто ждет освобождения дескриптора
     5         kx       и не пытается во время ожидания выполнять другие задачи.
     5         kx      */
     5         kx   }
     5         kx   return fd;
     5         kx }
     5         kx 
     5         kx static void __unlock_file( int fd )
     5         kx {
     5         kx   if( fd != -1 ) flock( fd, LOCK_UN );
     5         kx   /*
     5         kx     Здесь, в случае ошибки, мы не будем выводить
     5         kx     никаких сообщений. Наш процесс выполняет простую
     5         kx     атомарную задачу и, наверное, завершится в скором
     5         kx     времени, освободив все дескрипторы.
     5         kx    */
     5         kx }
     5         kx /*
     5         kx   End of LOCK FILE functions.
     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 enum _pkglog_type
     5         kx {
     5         kx   PKGLOG_TEXT = 0,
     5         kx   PKGLOG_GZ,
     5         kx   PKGLOG_BZ2,
     5         kx   PKGLOG_XZ,
     5         kx   PKGLOG_TAR,
     5         kx 
     5         kx   PKGLOG_UNKNOWN
     5         kx };
     5         kx 
     5         kx static enum _pkglog_type pkglog_type = PKGLOG_UNKNOWN;
     5         kx static char uncompress[2] = { 0, 0 };
     5         kx 
     5         kx 
     5         kx static enum _pkglog_type check_pkglog_file( 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   uncompress[0] = '\0';
     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     FATAL_ERROR( "Unknown type of input file %s", basename( (char *)fname ) );
     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 PKGLOG_TEXT;
     5         kx   }
     5         kx 
     5         kx   /* GZ */
     5         kx   if( buf[0] == 0x1F && buf[1] == 0x8B && buf[2] == 0x08 )
     5         kx   {
     5         kx     uncompress[0] = 'x';
     5         kx     close( fd ); return PKGLOG_GZ;
     5         kx   }
     5         kx 
     5         kx   /* BZ2 */
     5         kx   if( buf[0] == 0x42 && buf[1] == 0x5A && buf[2] == 0x68 )
     5         kx   {
     5         kx     uncompress[0] = 'j';
     5         kx     close( fd ); return PKGLOG_BZ2;
     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     uncompress[0] = 'J';
     5         kx     close( fd ); return PKGLOG_XZ;
     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 PKGLOG_TAR;
     5         kx     }
     5         kx   }
     5         kx 
     5         kx   close( fd ); return PKGLOG_UNKNOWN;
     5         kx }
     5         kx 
     5         kx 
     5         kx void get_args( int argc, char *argv[] )
     5         kx {
     5         kx   const char* short_options = "hvd:o:";
     5         kx 
     5         kx   const struct option long_options[] =
     5         kx   {
     5         kx     { "help",        no_argument,       NULL, 'h' },
     5         kx     { "version",     no_argument,       NULL, 'v' },
     5         kx     { "destination", required_argument, NULL, 'd' },
     5         kx     { "operation",   required_argument, NULL, 'o' },
     5         kx     { NULL,          0,                 NULL,  0  }
     5         kx   };
     5         kx 
     5         kx   int ret;
     5         kx   int option_index = 0;
     5         kx 
     5         kx   while( (ret = getopt_long( argc, argv, short_options, long_options, &option_index )) != -1 )
     5         kx   {
     5         kx     switch( ret )
     5         kx     {
     5         kx       case 'h':
     5         kx       {
     5         kx         usage();
     5         kx         break;
     5         kx       }
     5         kx       case 'v':
     5         kx       {
     5         kx         version();
     5         kx         break;
     5         kx       }
     5         kx 
     5         kx       case 'd':
     5         kx       {
     5         kx         if( optarg != NULL )
     5         kx         {
     5         kx           destination = xstrdup( (const char *)optarg );
     5         kx           remove_trailing_slash( destination );
     5         kx         }
     5         kx         else
     5         kx           /* option is present but without value */
     5         kx           usage();
     5         kx         break;
     5         kx       }
     5         kx       case 'o':
     5         kx       {
     5         kx         operation = xstrdup( (const char *)optarg );
     5         kx         to_lowercase( operation );
     5         kx         if( strcmp( operation, "inc" ) != 0 && strcmp( operation, "dec" ) != 0 )
     5         kx         {
     5         kx           ERROR( "Invalid '%s' operation requested", operation );
     5         kx           usage();
     5         kx         }
     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( operation == NULL )
     5         kx   {
     5         kx     usage();
     5         kx   }
     5         kx 
     5         kx   /* last command line argument is the LOGFILE */
     5         kx   if( optind < argc )
     5         kx   {
     5         kx     char *buf = NULL;
     5         kx 
     5         kx     buf = (char *)malloc( (size_t)PATH_MAX );
     5         kx     if( !buf ) { FATAL_ERROR( "Cannot allocate memory" ); }
     5         kx 
     5         kx     if( destination == NULL )
     5         kx     {
     5         kx       pkglog_fname = xstrdup( (const char *)argv[optind++] );
     5         kx       if( pkglog_fname == NULL )
     5         kx       {
     5         kx         FATAL_ERROR( "Unable to set input PKGLOG file name" );
     5         kx       }
     5         kx 
     5         kx       bzero( (void *)buf, PATH_MAX );
     5         kx       (void)sprintf( buf, "%s", pkglog_fname );
     5         kx       destination  = xstrdup( (const char *)dirname( buf ) );
     5         kx       if( destination == NULL )
     5         kx       {
     5         kx         FATAL_ERROR( "Unable to set destination directory" );
     5         kx       }
     5         kx 
     5         kx     }
     5         kx     else
     5         kx     {
     5         kx       bzero( (void *)buf, PATH_MAX );
     5         kx       (void)sprintf( buf, "%s/%s", destination, argv[optind++] );
     5         kx       pkglog_fname = xstrdup( (const char *)buf );
     5         kx       if( pkglog_fname == NULL )
     5         kx       {
     5         kx         FATAL_ERROR( "Unable to set inpit PKGLOG file name" );
     5         kx       }
     5         kx     }
     5         kx 
     5         kx     free( buf );
     5         kx 
     5         kx     pkglog_type = check_pkglog_file( (const char *)pkglog_fname );
     5         kx     if( pkglog_type != PKGLOG_TEXT )
     5         kx     {
     5         kx       ERROR( "%s: Unknown input file format", basename( pkglog_fname ) );
     5         kx       usage();
     5         kx     }
     5         kx 
     5         kx   }
     5         kx   else
     5         kx   {
     5         kx     usage();
     5         kx   }
     5         kx }
     5         kx 
     5         kx 
     5         kx /*
     5         kx   Especialy for pkginfo lines.
     5         kx   Remove leading spaces and take non-space characters only:
     5         kx  */
     5         kx static char *skip_spaces( char *s )
     5         kx {
     5         kx   char *q, *p = (char *)0;
     5         kx 
     5         kx   if( !s || *s == '\0' ) return p;
     5         kx 
     5         kx   p = s;
     5         kx 
     5         kx   while( (*p == ' ' || *p == '\t') && *p != '\0' ) { ++p; } q = p;
     5         kx   while(  *q != ' ' && *q != '\t'  && *q != '\0' ) { ++q; } *q = '\0';
     5         kx 
     5         kx   if( *p == '\0' ) return (char *)0;
     5         kx 
     5         kx   return( xstrdup( (const char *)p ) );
     5         kx }
     5         kx 
     5         kx 
     5         kx /*
     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 
     5         kx int get_pkginfo()
     5         kx {
     5         kx   int   ret = -1;
     5         kx   FILE *log = NULL;
     5         kx 
     5         kx   if( pkglog_fname != NULL )
     5         kx   {
     5         kx     log = fopen( (const char *)pkglog_fname, "r" );
     5         kx     if( !log )
     5         kx     {
     5         kx       FATAL_ERROR( "Cannot open %s file", pkglog_fname );
     5         kx     }
     5         kx   }
     5         kx 
     5         kx   if( log != NULL )
     5         kx   {
     5         kx     char *ln   = NULL;
     5         kx     char *line = NULL;
     5         kx 
     5         kx     char    *pkgname_pattern = "PACKAGE NAME:",
     5         kx              *pkgver_pattern = "PACKAGE VERSION:",
     5         kx               *group_pattern = "GROUP:",
     5         kx                *arch_pattern = "ARCH:",
     5         kx          *distroname_pattern = "DISTRO:",
     5         kx           *distrover_pattern = "DISTRO VERSION:";
     5         kx 
     5         kx     int last = 10; /* read first 10 lines only */
     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     ++ret;
     5         kx 
     5         kx     if( last )
     5         kx     {
     5         kx       int n = 1;
     5         kx 
     5         kx       while( (ln = fgets( line, PATH_MAX, log )) )
     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_pattern )) && match == ln ) /* at start of line only */
     5         kx         {
     5         kx           pkgname = skip_spaces( ln + strlen( pkgname_pattern ) );
     5         kx         }
     5         kx         if( (match = strstr( ln, pkgver_pattern )) && match == ln )
     5         kx         {
     5         kx           pkgver = skip_spaces( ln + strlen( pkgver_pattern ) );
     5         kx         }
     5         kx         if( (match = strstr( ln, arch_pattern )) && match == ln )
     5         kx         {
     5         kx           arch = skip_spaces( ln + strlen( arch_pattern ) );
     5         kx         }
     5         kx         if( (match = strstr( ln, distroname_pattern )) && match == ln )
     5         kx         {
     5         kx           distroname = skip_spaces( ln + strlen( distroname_pattern ) );
     5         kx         }
     5         kx         if( (match = strstr( ln, distrover_pattern )) && match == ln )
     5         kx         {
     5         kx           distrover = skip_spaces( ln + strlen( distrover_pattern ) );
     5         kx         }
     5         kx         if( (match = strstr( ln, group_pattern )) && match == ln )
     5         kx         {
     5         kx           group = skip_spaces( ln + strlen( group_pattern ) );
     5         kx         }
     5         kx 
     5         kx         if( n < last ) ++n;
     5         kx         else break;
     5         kx 
     5         kx       } /* End of while() */
     5         kx 
     5         kx     }
     5         kx 
     5         kx     free( line );
     5         kx 
     5         kx     if(    pkgname == NULL ) ++ret;
     5         kx     if(     pkgver == NULL ) ++ret;
     5         kx     if(       arch == NULL ) ++ret;
     5         kx     if( distroname == NULL ) ++ret;
     5         kx     if(  distrover == NULL ) ++ret;
     5         kx     /* group can be equal to NULL */
     5         kx 
     5         kx     fclose( log );
     5         kx   }
     5         kx 
     5         kx   return( ret );
     5         kx }
     5         kx 
     5         kx 
     5         kx 
     5         kx int get_references_section( int *start, int *stop, unsigned int *cnt, FILE *log )
     5         kx {
     5         kx   int ret = -1, found = 0;
     5         kx 
     5         kx   if( !start || !stop || !cnt ) return ret;
     5         kx 
     5         kx   if( log != NULL )
     5         kx   {
     5         kx     char *ln   = NULL;
     5         kx     char *line = NULL;
     5         kx 
     5         kx     line = (char *)malloc( (size_t)PATH_MAX );
     5         kx     if( !line )
     5         kx     {
     5         kx       FATAL_ERROR( "Cannot allocate memory" );
     5         kx     }
     5         kx 
     5         kx     ++ret;
     5         kx 
     5         kx     while( (ln = fgets( line, PATH_MAX, log )) )
     5         kx     {
     5         kx       char *match = NULL;
     5         kx 
     5         kx       if( (match = strstr( ln, "REFERENCE COUNTER:" )) && match == ln ) /* at start of line only */
     5         kx       {
     5         kx         *start = ret + 1;
     5         kx         ++found;
     5         kx 
     5         kx         /* Get reference counter */
     5         kx         {
     5         kx           unsigned int count;
     5         kx           int          rc;
     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           rc = sscanf( ln, "REFERENCE COUNTER: %u", &count );
     5         kx           if( rc == 1 && cnt != NULL )
     5         kx           {
     5         kx             *cnt = count;
     5         kx           }
     5         kx         }
     5         kx       }
     5         kx       if( (match = strstr( ln, "REQUIRES:" )) && match == ln )
     5         kx       {
     5         kx         *stop = ret + 1;
     5         kx         ++found;
     5         kx       }
     5         kx 
     5         kx       ++ret;
     5         kx     }
     5         kx 
     5         kx     free( line );
     5         kx 
     5         kx     ret = ( found == 2 ) ? 0 : 1; /* 0 - success; 1 - not found. */
     5         kx 
     5         kx     fseek( log, 0, SEEK_SET );
     5         kx   }
     5         kx 
     5         kx   return( ret );
     5         kx }
     5         kx 
     5         kx 
     5         kx 
     5         kx int get_requires_section( int *start, int *stop, const char *log_fname )
     5         kx {
     5         kx   int ret = -1, found = 0;
     5         kx 
     5         kx   FILE *log = NULL;
     5         kx 
     5         kx   if( !start || !stop ) return ret;
     5         kx 
     5         kx   if( log_fname != NULL )
     5         kx   {
     5         kx     log = fopen( (const char *)log_fname, "r" );
     5         kx     if( !log )
     5         kx     {
     5         kx       return ret;
     5         kx     }
     5         kx   }
     5         kx 
     5         kx   if( log != NULL )
     5         kx   {
     5         kx     char *ln   = NULL;
     5         kx     char *line = NULL;
     5         kx 
     5         kx     line = (char *)malloc( (size_t)PATH_MAX );
     5         kx     if( !line )
     5         kx     {
     5         kx       FATAL_ERROR( "Cannot allocate memory" );
     5         kx     }
     5         kx 
     5         kx     ++ret;
     5         kx 
     5         kx     while( (ln = fgets( line, PATH_MAX, log )) )
     5         kx     {
     5         kx       char *match = NULL;
     5         kx 
     5         kx       if( (match = strstr( ln, "REQUIRES:" )) && match == ln ) /* at start of line only */
     5         kx       {
     5         kx         *start = ret + 1;
     5         kx         ++found;
     5         kx       }
     5         kx       if( (match = strstr( ln, "PACKAGE DESCRIPTION:" )) && match == ln )
     5         kx       {
     5         kx         *stop = ret + 1;
     5         kx         ++found;
     5         kx       }
     5         kx 
     5         kx       ++ret;
     5         kx     }
     5         kx 
     5         kx     free( line );
     5         kx 
     5         kx     ret = ( found == 2 ) ? 0 : 1; /* 0 - success; 1 - not found. */
     5         kx 
     5         kx     fclose( log );
     5         kx   }
     5         kx 
     5         kx   return( ret );
     5         kx }
     5         kx 
     5         kx 
     5         kx int get_description_section( int *start, int *stop, const char *log_fname )
     5         kx {
     5         kx   int ret = -1, found = 0;
     5         kx 
     5         kx   FILE *log = NULL;
     5         kx 
     5         kx   if( !start || !stop ) return ret;
     5         kx 
     5         kx   if( log_fname != NULL )
     5         kx   {
     5         kx     log = fopen( (const char *)log_fname, "r" );
     5         kx     if( !log )
     5         kx     {
     5         kx       return ret;
     5         kx     }
     5         kx   }
     5         kx 
     5         kx   if( log != NULL )
     5         kx   {
     5         kx     char *ln   = NULL;
     5         kx     char *line = NULL;
     5         kx 
     5         kx     line = (char *)malloc( (size_t)PATH_MAX );
     5         kx     if( !line )
     5         kx     {
     5         kx       FATAL_ERROR( "Cannot allocate memory" );
     5         kx     }
     5         kx 
     5         kx     ++ret;
     5         kx 
     5         kx     while( (ln = fgets( line, PATH_MAX, log )) )
     5         kx     {
     5         kx       char *match = NULL;
     5         kx 
     5         kx       if( (match = strstr( ln, "PACKAGE DESCRIPTION:" )) && match == ln ) /* at start of line only */
     5         kx       {
     5         kx         *start = ret + 1;
     5         kx         ++found;
     5         kx       }
     5         kx       if( (match = strstr( ln, "RESTORE LINKS:" )) && match == ln )
     5         kx       {
     5         kx         *stop = ret + 1;
     5         kx         ++found;
     5         kx       }
     5         kx 
     5         kx       ++ret;
     5         kx     }
     5         kx 
     5         kx     free( line );
     5         kx 
     5         kx     ret = ( found == 2 ) ? 0 : 1; /* 0 - success; 1 - not found. */
     5         kx 
     5         kx     fclose( log );
     5         kx   }
     5         kx 
     5         kx   return( ret );
     5         kx }
     5         kx 
     5         kx 
     5         kx int get_restore_links_section( int *start, int *stop, const char *log_fname )
     5         kx {
     5         kx   int ret = -1, found = 0;
     5         kx 
     5         kx   FILE *log = NULL;
     5         kx 
     5         kx   if( !start || !stop ) return ret;
     5         kx 
     5         kx   if( log_fname != NULL )
     5         kx   {
     5         kx     log = fopen( (const char *)log_fname, "r" );
     5         kx     if( !log )
     5         kx     {
     5         kx       return ret;
     5         kx     }
     5         kx   }
     5         kx 
     5         kx   if( log != NULL )
     5         kx   {
     5         kx     char *ln   = NULL;
     5         kx     char *line = NULL;
     5         kx 
     5         kx     line = (char *)malloc( (size_t)PATH_MAX );
     5         kx     if( !line )
     5         kx     {
     5         kx       FATAL_ERROR( "Cannot allocate memory" );
     5         kx     }
     5         kx 
     5         kx     ++ret;
     5         kx 
     5         kx     while( (ln = fgets( line, PATH_MAX, log )) )
     5         kx     {
     5         kx       char *match = NULL;
     5         kx 
     5         kx       if( (match = strstr( ln, "RESTORE LINKS:" )) && match == ln ) /* at start of line only */
     5         kx       {
     5         kx         *start = ret + 1;
     5         kx         ++found;
     5         kx       }
     5         kx       if( (match = strstr( ln, "INSTALL SCRIPT:" )) && match == ln )
     5         kx       {
     5         kx         *stop = ret + 1;
     5         kx         ++found;
     5         kx       }
     5         kx 
     5         kx       ++ret;
     5         kx     }
     5         kx 
     5         kx     free( line );
     5         kx 
     5         kx     ret = ( found == 2 ) ? 0 : 1; /* 0 - success; 1 - not found. */
     5         kx 
     5         kx     fclose( log );
     5         kx   }
     5         kx 
     5         kx   return( ret );
     5         kx }
     5         kx 
     5         kx 
     5         kx int get_install_script_section( int *start, int *stop, const char *log_fname )
     5         kx {
     5         kx   int ret = -1, found = 0;
     5         kx 
     5         kx   FILE *log = NULL;
     5         kx 
     5         kx   if( !start || !stop ) return ret;
     5         kx 
     5         kx   if( log_fname != NULL )
     5         kx   {
     5         kx     log = fopen( (const char *)log_fname, "r" );
     5         kx     if( !log )
     5         kx     {
     5         kx       return ret;
     5         kx     }
     5         kx   }
     5         kx 
     5         kx   if( log != NULL )
     5         kx   {
     5         kx     char *ln   = NULL;
     5         kx     char *line = NULL;
     5         kx 
     5         kx     line = (char *)malloc( (size_t)PATH_MAX );
     5         kx     if( !line )
     5         kx     {
     5         kx       FATAL_ERROR( "Cannot allocate memory" );
     5         kx     }
     5         kx 
     5         kx     ++ret;
     5         kx 
     5         kx     while( (ln = fgets( line, PATH_MAX, log )) )
     5         kx     {
     5         kx       char *match = NULL;
     5         kx 
     5         kx       if( (match = strstr( ln, "INSTALL SCRIPT:" )) && match == ln ) /* at start of line only */
     5         kx       {
     5         kx         *start = ret + 1;
     5         kx         ++found;
     5         kx       }
     5         kx       if( (match = strstr( ln, "FILE LIST:" )) && match == ln )
     5         kx       {
     5         kx         *stop = ret + 1;
     5         kx         ++found;
     5         kx       }
     5         kx 
     5         kx       ++ret;
     5         kx     }
     5         kx 
     5         kx     free( line );
     5         kx 
     5         kx     ret = ( found == 2 ) ? 0 : 1; /* 0 - success; 1 - not found. */
     5         kx 
     5         kx     fclose( log );
     5         kx   }
     5         kx 
     5         kx   return( ret );
     5         kx }
     5         kx 
     5         kx 
     5         kx int get_file_list_section( int *start, int *stop, const char *log_fname )
     5         kx {
     5         kx   int ret = -1, found = 0;
     5         kx 
     5         kx   FILE *log = NULL;
     5         kx 
     5         kx   if( !start || !stop ) return ret;
     5         kx 
     5         kx   if( log_fname != NULL )
     5         kx   {
     5         kx     log = fopen( (const char *)log_fname, "r" );
     5         kx     if( !log )
     5         kx     {
     5         kx       return ret;
     5         kx     }
     5         kx   }
     5         kx 
     5         kx   if( log != NULL )
     5         kx   {
     5         kx     char *ln   = NULL;
     5         kx     char *line = NULL;
     5         kx 
     5         kx     line = (char *)malloc( (size_t)PATH_MAX );
     5         kx     if( !line )
     5         kx     {
     5         kx       FATAL_ERROR( "Cannot allocate memory" );
     5         kx     }
     5         kx 
     5         kx     ++ret;
     5         kx     *stop = 0;
     5         kx 
     5         kx     while( (ln = fgets( line, PATH_MAX, log )) )
     5         kx     {
     5         kx       char *match = NULL;
     5         kx 
     5         kx       if( (match = strstr( ln, "FILE LIST:" )) && match == ln ) /* at start of line only */
     5         kx       {
     5         kx         *start = ret + 1;
     5         kx         ++found;
     5         kx       }
     5         kx 
     5         kx       ++ret;
     5         kx     }
     5         kx 
     5         kx     free( line );
     5         kx 
     5         kx     ret = ( found == 1 ) ? 0 : 1; /* 0 - success; 1 - not found. */
     5         kx 
     5         kx     fclose( log );
     5         kx   }
     5         kx 
     5         kx   return( ret );
     5         kx }
     5         kx 
     5         kx 
     5         kx 
     5         kx 
     5         kx /***********************************************************
     5         kx   get_requires():
     5         kx   --------------
     5         kx     if( success ) - returns number of requires
     5         kx     if(   error ) - returns -1
     5         kx  */
     5         kx int get_requires( const char *requires, const char *log_fname )
     5         kx {
     5         kx   int   ret = -1;
     5         kx   FILE *req = NULL, *log = NULL;
     5         kx 
     5         kx   int   start = 0, stop  = 0;
     5         kx 
     5         kx   if( get_requires_section( &start, &stop, log_fname ) != 0 )
     5         kx   {
     5         kx     return ret;
     5         kx   }
     5         kx 
     5         kx   if( log_fname != NULL )
     5         kx   {
     5         kx     log = fopen( (const char *)log_fname, "r" );
     5         kx     if( ! log )
     5         kx     {
     5         kx       return ret;
     5         kx     }
     5         kx   }
     5         kx 
     5         kx   if( requires != NULL )
     5         kx   {
     5         kx     req = fopen( (const char *)requires, "w" );
     5         kx     if( ! req )
     5         kx     {
     5         kx       return ret;
     5         kx     }
     5         kx   }
     5         kx 
     5         kx   /* get PKGLOG sections */
     5         kx   {
     5         kx     char *ln   = NULL;
     5         kx     char *line = NULL;
     5         kx 
     5         kx     line = (char *)malloc( (size_t)PATH_MAX );
     5         kx     if( !line )
     5         kx     {
     5         kx       FATAL_ERROR( "Cannot allocate memory" );
     5         kx     }
     5         kx 
     5         kx     ++ret;
     5         kx 
     5         kx     if( start && start < stop )
     5         kx     {
     5         kx       int n = 1, lines = 0;
     5         kx 
     5         kx       while( (ln = fgets( line, PATH_MAX, log )) )
     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( (n > start) && (n < stop) )
     5         kx         {
     5         kx           fprintf( req, "%s\n", ln );
     5         kx           ++lines;
     5         kx         }
     5         kx         ++n;
     5         kx       }
     5         kx 
     5         kx       ret = lines; /* number of lines in the LIST */
     5         kx     }
     5         kx 
     5         kx     free( line );
     5         kx 
     5         kx     fclose( log );
     5         kx     fflush( req ); fclose( req );
     5         kx   }
     5         kx 
     5         kx   return( ret );
     5         kx }
     5         kx 
     5         kx 
     5         kx struct package **read_requires( const char *fname, int n )
     5         kx {
     5         kx   struct package **requires = (struct package **)0;
     5         kx   struct package **ptr      = (struct package **)0;
     5         kx 
     5         kx   FILE  *file;
     5         kx 
     5         kx   if( ! fname ) return NULL;
     5         kx 
     5         kx   requires = create_requires( n + 1 );
     5         kx   if( requires )
     5         kx   {
     5         kx     int   i;
     5         kx     char *ln   = NULL;
     5         kx     char *line = NULL;
     5         kx 
     5         kx     line = (char *)malloc( (size_t)PATH_MAX );
     5         kx     if( !line )
     5         kx     {
     5         kx       FATAL_ERROR( "Cannot allocate memory" );
     5         kx     }
     5         kx 
     5         kx     struct package *package;
     5         kx 
     5         kx     file = fopen( fname, "r" );
     5         kx     if( !file )
     5         kx     {
     5         kx       free( line );
     5         kx       free_requires( requires );
     5         kx       return NULL;
     5         kx     }
     5         kx 
     5         kx     ptr = requires;
     5         kx     for( i = 0; i < n; ++i )
     5         kx     {
     5         kx       if( (ln = fgets( line, PATH_MAX, file )) )
     5         kx       {
     5         kx         char *d, *name, *version;
     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         /* сut 'name=version' by delimiter '=': */
     5         kx         d = index( ln, '=' ); *d = '\0';
     5         kx         version = ++d; name = ln;
     5         kx 
     5         kx         package = create_package( name, version, arch, DISTRO_NAME, DISTRO_VERSION );
     5         kx         if( package )
     5         kx         {
     5         kx           *ptr = package;
     5         kx           ptr++;
     5         kx         }
     5         kx       }
     5         kx     }
     5         kx     *ptr = (struct package *)0;
     5         kx 
     5         kx     free( line );
     5         kx   }
     5         kx 
     5         kx   return requires;
     5         kx }
     5         kx 
     5         kx int find_requires( char *pname, const char *grp )
     5         kx {
     5         kx   char *name = NULL;
     5         kx   char *buf  = NULL;
     5         kx 
     5         kx   struct package **ptr = requires;
     5         kx 
     5         kx   buf = (char *)malloc( (size_t)PATH_MAX );
     5         kx 
     5         kx   if( !buf ) { FATAL_ERROR( "Cannot allocate memory" ); }
     5         kx 
     5         kx   while( *ptr )
     5         kx   {
     5         kx     if( grp && *grp != '\0' )
     5         kx     {
     5         kx       char   *p = NULL;
     5         kx       size_t  len = strlen( (*ptr)->name );
     5         kx 
     5         kx       if( (p = strstr( (*ptr)->name, grp )) && (p == (*ptr)->name) )
     5         kx       {
     5         kx         /* if group is equal to required package's group then remove group from the name */
     5         kx         name = alloca( len + 1 );
     5         kx         strcpy( name, (const char *)(*ptr)->name );
     5         kx 
     5         kx         name = index( name, '/' ) + 1;
     5         kx       }
     5         kx       else
     5         kx       {
     5         kx         /* if group is not equal to required package's group then add group to the name */
     5         kx         name = alloca( len + strlen( grp ) + 2 );
     5         kx         (void)sprintf( name, "%s/%s", grp, (const char *)(*ptr)->name );
     5         kx       }
     5         kx     }
     5         kx     else
     5         kx     {
     5         kx       name = (*ptr)->name;
     5         kx     }
     5         kx 
     5         kx     (void)sprintf( buf, "%s-%s-%s-%s-%s",
     5         kx                          name, (*ptr)->version, (*ptr)->arch,
     5         kx                          (*ptr)->distro_name, (*ptr)->distro_version );
     5         kx 
     5         kx     if( ! strcmp( buf, pname ) )
     5         kx     {
     5         kx       free( buf ); return 1;
     5         kx     }
     5         kx     ptr++;
     5         kx   }
     5         kx 
     5         kx   free( buf );
     5         kx 
     5         kx   return 0;
     5         kx }
     5         kx 
     5         kx 
     5         kx int save_tmp_head( FILE *log, int stop, const char *fname )
     5         kx {
     5         kx   FILE *fp;
     5         kx   int   ret = -1;
     5         kx 
     5         kx   char *ln   = NULL;
     5         kx   char *line = NULL;
     5         kx   int   n = 1, lines = 0;
     5         kx 
     5         kx   if( !stop || !log || !fname || *fname == '\0' ) return ret;
     5         kx 
     5         kx   fp = fopen( fname, "w" );
     5         kx   if( !fp )
     5         kx   {
     5         kx     return ret;
     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   ++ret;
     5         kx 
     5         kx   while( (ln = fgets( line, PATH_MAX, log )) )
     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( n < stop )
     5         kx     {
     5         kx       fprintf( fp, "%s\n", ln );
     5         kx       ++n; ++lines;
     5         kx     }
     5         kx     else
     5         kx       break;
     5         kx   }
     5         kx 
     5         kx   ret = lines; /* number of lines in the HEAD */
     5         kx 
     5         kx   free( line );
     5         kx 
     5         kx   fseek( log, 0, SEEK_SET );
     5         kx   fclose( fp );
     5         kx 
     5         kx   return ret;
     5         kx }
     5         kx 
     5         kx int save_tmp_tail( FILE *log, int start, const char *fname )
     5         kx {
     5         kx   FILE *fp;
     5         kx   int   ret = -1;
     5         kx 
     5         kx   char *ln   = NULL;
     5         kx   char *line = NULL;
     5         kx   int   n = 1, lines = 0;
     5         kx 
     5         kx   if( !start || !log || !fname || *fname == '\0' ) return ret;
     5         kx 
     5         kx   fp = fopen( fname, "w" );
     5         kx   if( !fp )
     5         kx   {
     5         kx     return ret;
     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   ++ret;
     5         kx 
     5         kx   while( (ln = fgets( line, PATH_MAX, log )) && (n < start) ) ++n;
     5         kx 
     5         kx   while( (ln = fgets( line, PATH_MAX, log )) )
     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     fprintf( fp, "%s\n", ln );
     5         kx     ++lines;
     5         kx   }
     5         kx 
     5         kx   ret = lines; /* number of lines in the TAIL */
     5         kx 
     5         kx   free( line );
     5         kx 
     5         kx   fseek( log, 0, SEEK_SET );
     5         kx   fclose( fp );
     5         kx 
     5         kx   return ret;
     5         kx }
     5         kx 
     5         kx int write_tmp_part( FILE *log, const char *fname )
     5         kx {
     5         kx   FILE *fp;
     5         kx   int   ret = -1;
     5         kx 
     5         kx   char *ln   = NULL;
     5         kx   char *line = NULL;
     5         kx   int   lines = 0;
     5         kx 
     5         kx   if( !log || !fname || *fname == '\0' ) return ret;
     5         kx 
     5         kx   fp = fopen( fname, "r" );
     5         kx   if( !fp )
     5         kx   {
     5         kx     return ret;
     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   ++ret;
     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     fprintf( log, "%s\n", ln );
     5         kx     ++lines;
     5         kx   }
     5         kx 
     5         kx   ret = lines; /* number of written lines */
     5         kx 
     5         kx   free( line );
     5         kx 
     5         kx   fclose( fp );
     5         kx 
     5         kx   return ret;
     5         kx }
     5         kx 
     5         kx 
     5         kx 
     5         kx static char **create_references( size_t size )
     5         kx {
     5         kx   char **references = (char **)0;
     5         kx 
     5         kx   if( size > 0 )
     5         kx   {
     5         kx     references = (char **)malloc( size * sizeof(char *) );
     5         kx     bzero( (void *)references, size * sizeof(char *) );
     5         kx   }
     5         kx 
     5         kx   return( references );
     5         kx }
     5         kx 
     5         kx static void free_references( char **references )
     5         kx {
     5         kx   if( references )
     5         kx   {
     5         kx     char **ptr = references;
     5         kx 
     5         kx     while( *ptr )
     5         kx     {
     5         kx       if( *ptr ) free( *ptr );
     5         kx       ptr++;
     5         kx     }
     5         kx     free( references );
     5         kx   }
     5         kx }
     5         kx 
     5         kx 
     5         kx static char **get_references( FILE *log, int start, unsigned int *cnt, char *grp, char *name, char *version )
     5         kx {
     5         kx   char **refs = (char **)0;
     5         kx   char **ptr;
     5         kx 
     5         kx   char *ln   = NULL;
     5         kx   char *line = NULL;
     5         kx   int   n = 1;
     5         kx 
     5         kx   size_t len = 0;
     5         kx 
     5         kx   unsigned int counter, pkgs;
     5         kx 
     5         kx   char *pkg = NULL;
     5         kx 
     5         kx   if( !log || !cnt || *cnt == 0 || !name || !version ) return refs;
     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   pkg = (char *)malloc( (size_t)PATH_MAX );
     5         kx   if( !pkg )
     5         kx   {
     5         kx     FATAL_ERROR( "Cannot allocate memory" );
     5         kx   }
     5         kx 
     5         kx   counter = *cnt;
     5         kx 
     5         kx   if( grp && *grp != '\0' ) { (void)sprintf( pkg, "%s/%s=", grp, name ); }
     5         kx   else                      { (void)sprintf( pkg, "%s=", name );         }
     5         kx 
     5         kx   len = strlen( pkg );
     5         kx 
     5         kx   refs = ptr = create_references( counter + 1 ); /* null terminated char *references[] */
     5         kx 
     5         kx   while( (ln = fgets( line, PATH_MAX, log )) && (n < start) ) ++n;
     5         kx 
     5         kx   n = 0; pkgs = 0;
     5         kx   while( (ln = fgets( line, PATH_MAX, log )) )
     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( strstr( ln, "REQUIRES:" ) ) break; /* if cnt greater than real number of references */
     5         kx 
     5         kx     if( n < counter )
     5         kx     {
     5         kx       if( strncmp( ln, pkg, len ) ) /* always remove 'name=version' from list */
     5         kx       {
     5         kx         if( refs )
     5         kx         {
     5         kx           *ptr = xstrdup( (const char *)ln ); ++ptr;
     5         kx           *ptr = (char *)0;
     5         kx           ++pkgs;
     5         kx         }
     5         kx       }
     5         kx       ++n;
     5         kx     }
     5         kx     else
     5         kx       break;
     5         kx   }
     5         kx 
     5         kx   free( line ); free( pkg );
     5         kx 
     5         kx   fseek( log, 0, SEEK_SET );
     5         kx 
     5         kx   if( pkgs == 0 )
     5         kx   {
     5         kx     free_references( refs );
     5         kx     refs = (char **)0;
     5         kx   }
     5         kx 
     5         kx   *cnt = pkgs;
     5         kx 
     5         kx   return refs;
     5         kx }
     5         kx 
     5         kx 
     5         kx static void _change_references( char *grp, char *name, char *version, const char *log_fname )
     5         kx {
     5         kx   int    fd;
     5         kx   FILE  *log;
     5         kx 
     5         kx   char  *head_fname = NULL, *tail_fname = NULL;
     5         kx   int    head_lines, tail_lines;
     5         kx 
     5         kx   int          rc, start, stop;
     5         kx   unsigned int counter;
     5         kx 
     5         kx   int    inc = ( strcmp( operation, "inc" ) == 0 ) ? 1 : 0;
     5         kx 
     5         kx   char **references = NULL;
     5         kx 
     5         kx   if( !name || !version || log_fname == NULL ) return;
     5         kx   if( check_pkglog_file( log_fname ) != PKGLOG_TEXT ) return;
     5         kx 
     5         kx   log = fopen( (const char *)log_fname, "r+" );
     5         kx   if( !log )
     5         kx   {
     5         kx     ERROR( "Cannot access %s file: %s", log_fname, strerror( errno ) );
     5         kx     return;
     5         kx   }
     5         kx 
     5         kx   fd = __lock_file( log );
     5         kx 
     5         kx   rc = get_references_section( &start, &stop, &counter, log );
     5         kx   if( rc != 0 )
     5         kx   {
     5         kx     ERROR( "%s: PKGLOG doesn't contains REFERENCE COUNTER section", log_fname );
     5         kx     __unlock_file( fd ); fclose( log );
     5         kx     return;
     5         kx   }
     5         kx 
     5         kx   head_fname = (char *)alloca( strlen( tmpdir ) + 7 );
     5         kx   (void)sprintf( head_fname, "%s/.HEAD", tmpdir );
     5         kx 
     5         kx   tail_fname = (char *)alloca( strlen( tmpdir ) + 7 );
     5         kx   (void)sprintf( tail_fname, "%s/.TAIL", tmpdir );
     5         kx 
     5         kx   head_lines = save_tmp_head( log, start, (const char *)head_fname );
     5         kx   tail_lines = save_tmp_tail( log, stop - 1, (const char *)tail_fname );
     5         kx 
     5         kx   if( head_lines < 10 && tail_lines < 12 )
     5         kx   {
     5         kx     ERROR( "%s: Invalid PKGLOG file", log_fname );
     5         kx     __unlock_file( fd ); fclose( log );
     5         kx     return;
     5         kx   }
     5         kx 
     5         kx   references = get_references( log, start, &counter, grp, name, version );
     5         kx 
     5         kx   if( ftruncate( fd, 0 ) != 0 )
     5         kx   {
     5         kx     ERROR( "Cannot change REFERENCE COUNTER in the %s file: %s", log_fname, strerror( errno ) );
     5         kx     free_references( references );
     5         kx     __unlock_file( fd ); fclose( log );
     5         kx     return;
     5         kx   }
     5         kx 
     5         kx   head_lines = write_tmp_part( log, (const char *)head_fname );
     5         kx 
     5         kx   if( inc ) ++counter;
     5         kx   fprintf( log, "REFERENCE COUNTER: %u\n", counter );
     5         kx   if( inc )
     5         kx   {
     5         kx     if( grp && *grp != '\0' )
     5         kx     {
     5         kx       fprintf( log, "%s/%s=%s\n", grp, name, version );
     5         kx     }
     5         kx     else
     5         kx     {
     5         kx       fprintf( log, "%s=%s\n", name, version );
     5         kx     }
     5         kx   }
     5         kx 
     5         kx   if( references )
     5         kx   {
     5         kx     char **ptr = references;
     5         kx 
     5         kx     while( *ptr )
     5         kx     {
     5         kx       if( *ptr ) fprintf( log, "%s\n", *ptr );
     5         kx       ptr++;
     5         kx     }
     5         kx 
     5         kx     free_references( references );
     5         kx   }
     5         kx 
     5         kx   tail_lines = write_tmp_part( log, (const char *)tail_fname );
     5         kx 
     5         kx   __unlock_file( fd );
     5         kx   fclose( log );
     5         kx }
     5         kx 
     5         kx 
     5         kx static void _search_required_packages( 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   if( stat( dirpath, &path_sb ) == -1 )
     5         kx   {
     5         kx     FATAL_ERROR( "%s: Cannot stat Setup Database or destination directory", dirpath );
     5         kx   }
     5         kx 
     5         kx   if( S_ISDIR(path_sb.st_mode) == 0 )
     5         kx   {
     5         kx     FATAL_ERROR( "%s: Setup Database or destination is not a directory", dirpath );
     5         kx   }
     5         kx 
     5         kx   if( (dir = opendir(dirpath) ) == NULL )
     5         kx   {
     5         kx     FATAL_ERROR( "Canot access %s directory: %s", dirpath, strerror( errno ) );
     5         kx   }
     5         kx 
     5         kx   len = strlen( dirpath );
     5         kx 
     5         kx   while( (entry = readdir( dir )) != NULL)
     5         kx   {
     5         kx     /* skip entries '.' and '..' */
     5         kx     if( ! strcmp( entry->d_name, "." ) || ! strcmp( entry->d_name, ".." ) ) continue;
     5         kx 
     5         kx     /* determinate a full name of an entry */
     5         kx     path = alloca( len + strlen( entry->d_name ) + 2 );
     5         kx 
     5         kx     strcpy( path, dirpath );
     5         kx     strcat( path, "/" );
     5         kx     strcat( path, entry->d_name );
     5         kx 
     5         kx     if( stat( path, &entry_sb ) == 0 )
     5         kx     {
     5         kx       if( S_ISREG(entry_sb.st_mode) )
     5         kx       {
     5         kx         if( find_requires( entry->d_name, grp ) )
     5         kx         {
     5         kx           _change_references( group, pkgname, pkgver, (const char *)path );
     5         kx         }
     5         kx       }
     5         kx       if( S_ISDIR(entry_sb.st_mode) && grp == NULL )
     5         kx       {
     5         kx         _search_required_packages( (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 
     5         kx   closedir( dir );
     5         kx }
     5         kx 
     5         kx 
     5         kx 
     5         kx 
     5         kx /*********************************************
     5         kx   Get directory where this program is placed:
     5         kx  */
     5         kx char *get_selfdir( void )
     5         kx {
     5         kx   char    *buf = NULL;
     5         kx   ssize_t  len;
     5         kx 
     5         kx   buf = (char *)malloc( 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   int    ret;
     5         kx 
     5         kx   set_signal_handlers();
     5         kx 
     5         kx   gid = getgid();
     5         kx   setgroups( 1, &gid );
     5         kx 
     5         kx   fatal_error_hook = fatal_error_actions;
     5         kx 
     5         kx   selfdir = get_selfdir();
     5         kx 
     5         kx   errlog = stderr;
     5         kx 
     5         kx   program = basename( argv[0] );
     5         kx   get_args( argc, argv );
     5         kx 
     5         kx   /* set_stack_size(); */
     5         kx 
     5         kx   exit_status = get_pkginfo();
     5         kx   if( exit_status != 0 )
     5         kx   {
     5         kx     FATAL_ERROR( "%s: Invalid input PKGLOG file", basename( pkglog_fname ) );
     5         kx   }
     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   else
     5         kx   {
     5         kx     char *buf = NULL;
     5         kx 
     5         kx     buf = (char *)malloc( (size_t)strlen( tmpdir ) + 11 );
     5         kx     if( !buf ) { FATAL_ERROR( "Cannot allocate memory" ); }
     5         kx 
     5         kx     (void)sprintf( (char *)&buf[0], "%s/.REQUIRES", tmpdir );
     5         kx     requires_fname = xstrdup( (const char *)&buf[0] );
     5         kx     free( buf );
     5         kx   }
     5         kx 
     5         kx   /*******************
     5         kx     Getting REQUIRES:
     5         kx    */
     5         kx   if( (ret = get_requires( (const char *)requires_fname, (const char *)pkglog_fname )) > 0 )
     5         kx   {
     5         kx     /* We have non-empty list of REQUIRES in the 'requires_fname' file */
     5         kx     requires = read_requires( (const char *)requires_fname, ret );
     5         kx     _search_required_packages( (const char *)destination, NULL );
     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 }