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