summaryrefslogtreecommitdiff
path: root/src/pkglist.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/pkglist.c')
-rw-r--r--src/pkglist.c2198
1 files changed, 2198 insertions, 0 deletions
diff --git a/src/pkglist.c b/src/pkglist.c
new file mode 100644
index 0000000..23b080d
--- /dev/null
+++ b/src/pkglist.c
@@ -0,0 +1,2198 @@
+
+/**********************************************************************
+
+ 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 <string.h>
+#include <linux/limits.h>
+#include <libgen.h> /* basename(3) */
+#include <unistd.h>
+#include <time.h>
+#include <math.h>
+
+#include <msglog.h>
+#include <wrapper.h>
+
+#include <make-pkglist.h>
+
+#include <cmpvers.h>
+#include <dlist.h>
+#include <btree.h>
+#include <jsmin.h>
+#include <pkglist.h>
+
+
+char *htmlroot = NULL;
+char *hardware = NULL;
+int minimize = 0;
+
+struct dlist *srcpkgs = NULL;
+
+struct dlist *packages = NULL;
+struct dlist *tarballs = NULL;
+
+struct dlist *provides = NULL;
+struct dlist *extern_requires = NULL;
+
+static struct dlist *tree = NULL;
+
+static char *pkgs_fname = NULL,
+ *tree_fname = NULL,
+ *html_fname = NULL;
+
+static char *pkgs_min_fname = NULL,
+ *tree_min_fname = NULL;
+
+static const char *tarball_suffix = "txz";
+
+/***************************************************************
+ tarballs List functions:
+ =======================
+
+ NOTE:
+ ----
+ TARBALLS is an optional list created in case when we have
+ a set of PACKAGES as input of make-pkglist utility. When we
+ are working with a set of input PKGLOGs the TARBALLS list
+ is not chreated and pointer to the tarballs == NULL.
+ */
+void add_tarball( char *tarball )
+{
+ tarballs = dlist_append( tarballs, (void *)xstrdup( (const char *)tarball ) );
+}
+
+static void __free_tarball( void *data, void *user_data )
+{
+ if( data ) { free( data ); }
+}
+
+void free_tarballs( void )
+{
+ if( tarballs ) { dlist_free( tarballs, __free_tarball ); tarballs = NULL; }
+}
+
+static int __compare_tarballs( const void *a, const void *b )
+{
+ return strncmp( (const char *)a, (const char *)b, (size_t)strlen((const char *)b) );
+}
+
+const char *find_tarball( const char *name )
+{
+ struct dlist *node = NULL;
+
+ if( !tarballs || !name ) return NULL;
+
+ node = dlist_find_data( tarballs, __compare_tarballs, (const void *)name );
+ if( node )
+ {
+ return (const char *)node->data;
+ }
+
+ return NULL;
+}
+
+/*********************
+ Just for debugging:
+ */
+static void __print_tarball( void *data, void *user_data )
+{
+ int *counter = (int *)user_data;
+
+ if( counter ) { fprintf( stdout, "tarball[%.5d]: %s\n", *counter, (char *)data ); ++(*counter); }
+ else { fprintf( stdout, "tarball: %s\n", (char *)data ); }
+}
+
+void print_tarballs( void )
+{
+ int cnt = 0;
+ if( tarballs ) { dlist_foreach( tarballs, __print_tarball, (void *)&cnt ); }
+}
+/*
+ End of tarballs List functions.
+ ***************************************************************/
+
+
+
+char *strprio( enum _priority priority, int short_name )
+{
+ char *p = NULL;
+
+ switch( priority )
+ {
+ case REQUIRED:
+ p = ( short_name ) ? "REQ" : "REQUIRED";
+ break;
+ case RECOMMENDED:
+ p = ( short_name ) ? "REC" : "RECOMMENDED";
+ break;
+ case OPTIONAL:
+ p = ( short_name ) ? "OPT" : "OPTIONAL";
+ break;
+ case SKIP:
+ p = ( short_name ) ? "SKP" : "SKIP";
+ break;
+ }
+ return p;
+}
+
+char *strproc( enum _procedure procedure )
+{
+ char *p = NULL;
+
+ switch( procedure )
+ {
+ case INSTALL:
+ p = "install";
+ break;
+ case UPDATE:
+ p = "update";
+ break;
+ }
+ return p;
+}
+
+
+/***************************************************************
+ PACKAGE functions:
+ */
+
+struct pkg *pkg_alloc( void )
+{
+ struct pkg *pkg = NULL;
+
+ pkg = (struct pkg *)malloc( sizeof( struct pkg ) );
+ if( !pkg ) { FATAL_ERROR( "Cannot allocate memory" ); }
+ bzero( (void *)pkg, sizeof( struct pkg ) );
+
+ return pkg;
+}
+
+void pkg_free( struct pkg *pkg )
+{
+ if( pkg )
+ {
+ if( pkg->group ) { free( pkg->group ); pkg->group = NULL; }
+ if( pkg->name ) { free( pkg->name ); pkg->name = NULL; }
+ if( pkg->version ) { free( pkg->version ); pkg->version = NULL; }
+
+ free( pkg );
+ }
+}
+
+static void __pkg_free_func( void *data, void *user_data )
+{
+ struct pkg *pkg = (struct pkg *)data;
+ if( pkg ) { pkg_free( pkg ); }
+}
+
+void free_srcpkgs( void )
+{
+ if( srcpkgs ) { dlist_free( srcpkgs, __pkg_free_func ); srcpkgs = NULL; }
+}
+
+void add_srcpkg( struct pkg *pkg )
+{
+ srcpkgs = dlist_append( srcpkgs, (void *)pkg );
+}
+
+
+static struct pkginfo *__pkginfo_alloc( void )
+{
+ struct pkginfo *pkginfo = NULL;
+
+ pkginfo = (struct pkginfo *)malloc( sizeof( struct pkginfo ) );
+ if( !pkginfo ) { FATAL_ERROR( "Cannot allocate memory" ); }
+ bzero( (void *)pkginfo, sizeof( struct pkginfo ) );
+
+ return pkginfo;
+}
+
+static void __pkginfo_free( struct pkginfo *pkginfo )
+{
+ if( pkginfo )
+ {
+ if( pkginfo->name ) { free( pkginfo->name ); pkginfo->name = NULL; }
+ if( pkginfo->version ) { free( pkginfo->version ); pkginfo->version = NULL; }
+ if( pkginfo->arch ) { free( pkginfo->arch ); pkginfo->arch = NULL; }
+ if( pkginfo->distro_name ) { free( pkginfo->distro_name ); pkginfo->distro_name = NULL; }
+ if( pkginfo->distro_version ) { free( pkginfo->distro_version ); pkginfo->distro_version = NULL; }
+ if( pkginfo->group ) { free( pkginfo->group ); pkginfo->group = NULL; }
+ if( pkginfo->short_description ) { free( pkginfo->short_description ); pkginfo->short_description = NULL; }
+ if( pkginfo->url ) { free( pkginfo->url ); pkginfo->url = NULL; }
+ if( pkginfo->license ) { free( pkginfo->license ); pkginfo->license = NULL; }
+
+ free( pkginfo );
+ }
+}
+
+
+static struct references *__references_alloc( void )
+{
+ struct references *references = NULL;
+
+ references = (struct references *)malloc( sizeof( struct references ) );
+ if( !references ) { FATAL_ERROR( "Cannot allocate memory" ); }
+ bzero( (void *)references, sizeof( struct references ) );
+
+ return references;
+}
+
+static void __references_free( struct references *references )
+{
+ if( references )
+ {
+ if( references->list ) { dlist_free( references->list, __pkg_free_func ); references->list = NULL; }
+ free( references );
+ }
+}
+
+
+static struct requires *__requires_alloc( void )
+{
+ struct requires *requires = NULL;
+
+ requires = (struct requires *)malloc( sizeof( struct requires ) );
+ if( !requires ) { FATAL_ERROR( "Cannot allocate memory" ); }
+ bzero( (void *)requires, sizeof( struct requires ) );
+
+ return requires;
+}
+
+static void __requires_free( struct requires *requires )
+{
+ if( requires )
+ {
+ if( requires->list ) { dlist_free( requires->list, __pkg_free_func ); requires->list = NULL; }
+ free( requires );
+ }
+}
+
+
+static struct files *__files_alloc( void )
+{
+ struct files *files = NULL;
+
+ files = (struct files *)malloc( sizeof( struct files ) );
+ if( !files ) { FATAL_ERROR( "Cannot allocate memory" ); }
+ bzero( (void *)files, sizeof( struct files ) );
+
+ return files;
+}
+
+static void __files_free_func( void *data, void *user_data )
+{
+ if( data ) { free( data ); }
+}
+
+static void __files_free( struct files *files )
+{
+ if( files )
+ {
+ if( files->list ) { dlist_free( files->list, __files_free_func ); files->list = NULL; }
+ free( files );
+ }
+}
+
+
+struct package *package_alloc( void )
+{
+ struct package *package = NULL;
+ struct pkginfo *pkginfo = __pkginfo_alloc();
+ struct references *references = __references_alloc();
+ struct requires *requires = __requires_alloc();
+ struct files *files = __files_alloc();
+
+ package = (struct package *)malloc( sizeof( struct package ) );
+ if( !package ) { FATAL_ERROR( "Cannot allocate memory" ); }
+ bzero( (void *)package, sizeof( struct package ) );
+
+ package->pkginfo = pkginfo;
+ package->references = references;
+ package->requires = requires;
+ package->files = files;
+
+ return package;
+}
+
+void package_free( struct package *package )
+{
+ if( package )
+ {
+ if( package->pkginfo ) { __pkginfo_free( package->pkginfo ); package->pkginfo = NULL; }
+ if( package->references ) { __references_free( package->references ); package->references = NULL; }
+ if( package->requires ) { __requires_free( package->requires ); package->requires = NULL; }
+ if( package->files ) { __files_free( package->files ); package->files = NULL; }
+
+ if( package->description ) { free( package->description ); package->description = NULL; }
+ if( package->restore_links ) { free( package->restore_links ); package->restore_links = NULL; }
+ if( package->install_script ) { free( package->install_script ); package->install_script = NULL; }
+ if( package->hardware ) { free( package->hardware ); package->hardware = NULL; }
+ if( package->tarball ) { free( package->tarball ); package->tarball = NULL; }
+
+ free( package );
+ }
+}
+
+static void __package_free_func( void *data, void *user_data )
+{
+ struct package *package = (struct package *)data;
+ if( package ) { package_free( package ); }
+}
+
+void free_packages( void )
+{
+ if( packages ) { dlist_free( packages, __package_free_func ); packages = NULL; }
+}
+
+
+static int __compare_packages( const void *a, const void *b )
+{
+ int ret = -1;
+
+ struct package *pkg1 = (struct package *)a;
+ struct package *pkg2 = (struct package *)b;
+
+ if( pkg1->pkginfo->group && pkg2->pkginfo->group )
+ {
+ ret = strcmp( pkg1->pkginfo->group, pkg2->pkginfo->group );
+ }
+ else if( !pkg1->pkginfo->group && !pkg2->pkginfo->group )
+ {
+ ret = 0;
+ }
+ else if( pkg1->pkginfo->group )
+ {
+ ret = 1;
+ }
+
+ if( ! ret )
+ {
+ return strcmp( pkg1->pkginfo->name, pkg2->pkginfo->name );
+ }
+ return ret;
+}
+
+static int __compare_packages_with_version( const void *a, const void *b )
+{
+ int ret = -1;
+
+ struct package *pkg1 = (struct package *)a;
+ struct package *pkg2 = (struct package *)b;
+
+ if( pkg1->pkginfo->group && pkg2->pkginfo->group )
+ {
+ ret = strcmp( pkg1->pkginfo->group, pkg2->pkginfo->group );
+ }
+ else if( !pkg1->pkginfo->group && !pkg2->pkginfo->group )
+ {
+ ret = 0;
+ }
+ else if( pkg1->pkginfo->group )
+ {
+ ret = 1;
+ }
+
+ if( ! ret )
+ {
+ ret = strcmp( pkg1->pkginfo->name, pkg2->pkginfo->name );
+ if( ! ret )
+ {
+ return cmp_version( (const char *)pkg1->pkginfo->version, (const char *)pkg2->pkginfo->version );
+ }
+ }
+ return ret;
+}
+
+
+void add_package( struct package *package )
+{
+ packages = dlist_append( packages, (void *)package );
+}
+
+void add_reference( struct package *package, struct pkg *pkg )
+{
+ if( package && package->references && pkg )
+ {
+ package->references->list = dlist_append( package->references->list, (void *)pkg );
+ package->references->size = dlist_length( package->references->list );
+ }
+}
+
+void add_required( struct package *package, struct pkg *pkg )
+{
+ if( package && package->requires && pkg )
+ {
+ package->requires->list = dlist_append( package->requires->list, (void *)pkg );
+ package->requires->size = dlist_length( package->requires->list );
+ }
+}
+
+void add_file( struct package *package, const char *fname )
+{
+ if( package && package->files && fname )
+ {
+ package->files->list = dlist_append( package->files->list, (void *)xstrdup( (const char *)fname ) );
+ package->files->size = dlist_length( package->files->list );
+ }
+}
+
+/*********************
+ Just for debugging:
+ */
+static void __print_reference( void *data, void *user_data )
+{
+ struct pkg *pkg = (struct pkg *)data;
+
+ if( pkg )
+ {
+ if( pkg->group ) { fprintf( stdout, "reference: %s/%s=%s\n", pkg->group, pkg->name, pkg->version ); }
+ else { fprintf( stdout, "reference: %s=%s\n", pkg->name, pkg->version ); }
+ }
+}
+
+void package_print_references( struct package *package )
+{
+ if( !package ) return;
+
+ if( package->references->list )
+ {
+ dlist_foreach( package->references->list, __print_reference, NULL );
+ }
+}
+
+static void __print_required( void *data, void *user_data )
+{
+ struct pkg *pkg = (struct pkg *)data;
+
+ if( pkg )
+ {
+ if( pkg->group ) { fprintf( stdout, "required: %s/%s=%s\n", pkg->group, pkg->name, pkg->version ); }
+ else { fprintf( stdout, "required: %s=%s\n", pkg->name, pkg->version ); }
+ }
+}
+
+void package_print_requires( struct package *package )
+{
+ if( !package ) return;
+
+ if( package->requires->list )
+ {
+ dlist_foreach( package->requires->list, __print_required, NULL );
+ }
+}
+
+static void __print_file( void *data, void *user_data )
+{
+ int *counter = (int *)user_data;
+
+ if( counter ) { fprintf( stdout, "file[%.5d]: %s\n", *counter, (char *)data ); ++(*counter); }
+ else { fprintf( stdout, "file: %s\n", (char *)data ); }
+}
+
+void package_print_files( struct package *package )
+{
+ int cnt = 0;
+
+ if( !package ) return;
+
+ if( package->files->list )
+ {
+ dlist_foreach( package->files->list, __print_file, (void *)&cnt );
+ }
+}
+
+/*
+ End of PACKAGES functions.
+ ***************************************************************/
+
+/***************************************************************
+ Extern REQUIRES list functions:
+ */
+
+static int __compare_required( const void *a, const void *b )
+{
+ int ret = -1;
+
+ struct pkg *pkg1 = (struct pkg *)a;
+ struct pkg *pkg2 = (struct pkg *)b;
+
+ if( pkg1->group && pkg2->group )
+ {
+ ret = strcmp( pkg1->group, pkg2->group );
+ }
+ else if( !pkg1->group && !pkg2->group )
+ {
+ ret = 0;
+ }
+ else if( pkg1->group )
+ {
+ ret = 1;
+ }
+
+ if( ! ret )
+ {
+ return strcmp( pkg1->name, pkg2->name );
+ }
+ return ret;
+}
+
+static int __compare_required_with_version( const void *a, const void *b )
+{
+ int ret = -1;
+
+ struct pkg *pkg1 = (struct pkg *)a;
+ struct pkg *pkg2 = (struct pkg *)b;
+
+ if( pkg1->group && pkg2->group )
+ {
+ ret = strcmp( pkg1->group, pkg2->group );
+ }
+ else if( !pkg1->group && !pkg2->group )
+ {
+ ret = 0;
+ }
+ else if( pkg1->group )
+ {
+ ret = 1;
+ }
+
+ if( ! ret )
+ {
+ ret = strcmp( pkg1->name, pkg2->name );
+ if( ! ret )
+ {
+ return cmp_version( (const char *)pkg1->version, (const char *)pkg2->version );
+ }
+ }
+ return ret;
+}
+
+static void __add_unique_required( void *data, void *user_data )
+{
+ struct pkg *pkg = (struct pkg *)data;
+
+ if( pkg )
+ {
+ struct dlist *found = dlist_find_data( extern_requires, __compare_required, (const void *)data );
+
+ if( found )
+ {
+ if( cmp_version( (const char *)((struct pkg *)found->data)->version, (const char *)pkg->version ) )
+ {
+ char *s = ((struct pkg *)found->data)->version;
+ ((struct pkg *)found->data)->version =
+ xstrdup( (const char *)max_version( (const char *)((struct pkg *)found->data)->version,
+ (const char *)pkg->version ) );
+ free( s );
+ }
+ }
+ else
+ {
+ struct pkg *req = pkg_alloc();
+ if( req )
+ {
+ if( pkg->group )
+ {
+ req->group = xstrdup( (const char *)pkg->group );
+ }
+ req->name = xstrdup( (const char *)pkg->name );
+ req->version = xstrdup( (const char *)pkg->version );
+
+ extern_requires = dlist_append( extern_requires, (void *)req );
+ }
+ }
+ }
+}
+
+static void __fill_extern_requires( void *data, void *user_data )
+{
+ struct package *package = (struct package *)data;
+
+ if( package )
+ {
+ struct pkg *provide = pkg_alloc();
+
+ if( provide )
+ {
+ if( package->pkginfo->group )
+ {
+ provide->group = xstrdup( (const char *)package->pkginfo->group );
+ }
+ provide->name = xstrdup( (const char *)package->pkginfo->name );
+ provide->version = xstrdup( (const char *)package->pkginfo->version );
+
+ provides = dlist_append( provides, (void *)provide );
+ }
+
+ if( package->requires->list )
+ {
+ dlist_foreach( package->requires->list, __add_unique_required, NULL );
+ }
+ }
+}
+
+static void __clean_extern_requires( void *data, void *user_data )
+{
+ if( data )
+ {
+ extern_requires = dlist_remove_data( extern_requires, __compare_required_with_version, __pkg_free_func, (const void *)data );
+ }
+}
+
+static int __compare_provided_old_package( const void *a, const void *b )
+{
+ int ret = -1;
+
+ struct package *pkg1 = (struct package *)a;
+ struct pkg *pkg2 = (struct pkg *)b;
+
+ if( pkg1->pkginfo->group && pkg2->group )
+ {
+ ret = strcmp( pkg1->pkginfo->group, pkg2->group );
+ }
+ else if( !pkg1->pkginfo->group && !pkg2->group )
+ {
+ ret = 0;
+ }
+ else if( pkg1->pkginfo->group )
+ {
+ ret = 1;
+ }
+
+ if( ! ret )
+ {
+ ret = strcmp( pkg1->pkginfo->name, pkg2->name );
+ if( ! ret )
+ {
+ pkg2->procedure = UPDATE; /* mark as too old */
+ return ret;
+ }
+ }
+ return ret;
+}
+
+static void __remove_old_package( void *data, void *user_data )
+{
+ packages = dlist_remove_data( packages, __compare_provided_old_package, __package_free_func, (const void *)data );
+}
+
+static void remove_old_packages( void )
+{
+ dlist_foreach( extern_requires, __remove_old_package, NULL );
+}
+/*
+ End of Extern REQUIRES list functions.
+ ***************************************************************/
+
+
+/***************************************************************
+ Check REQUIRES functions:
+ */
+static int __compare_provided( const void *a, const void *b )
+{
+ int ret = -1;
+
+ struct package *pkg1 = (struct package *)a;
+ struct pkg *pkg2 = (struct pkg *)b;
+
+ if( pkg1->pkginfo->group && pkg2->group )
+ {
+ ret = strcmp( pkg1->pkginfo->group, pkg2->group );
+ }
+ else if( !pkg1->pkginfo->group && !pkg2->group )
+ {
+ ret = 0;
+ }
+ else if( pkg1->pkginfo->group )
+ {
+ ret = 1;
+ }
+
+ if( ! ret )
+ {
+ return strcmp( pkg1->pkginfo->name, pkg2->name );
+ }
+ return ret;
+}
+
+static int __compare_packages_by_name( const void *a, const void *b )
+{
+ int ret = -1;
+
+ struct package *pkg1 = (struct package *)a;
+ struct package *pkg2 = (struct package *)b;
+
+ if( !strcmp( pkg1->pkginfo->name, pkg2->pkginfo->name ) )
+ {
+ if( pkg1->pkginfo->group && pkg2->pkginfo->group )
+ {
+ ret = strcmp( pkg1->pkginfo->group, pkg2->pkginfo->group );
+ }
+ else if( !pkg1->pkginfo->group && !pkg2->pkginfo->group )
+ {
+ ret = 0;
+ }
+ else if( pkg1->pkginfo->group )
+ {
+ ret = 1;
+ }
+
+ /* returns equal only if groups are not equal */
+ if( ret ) return 0;
+ }
+
+ return ret;
+}
+
+static int check_dependencies( struct package *package )
+{
+ struct dlist *list = NULL, *next = NULL, *update = NULL;
+ int depended = -1;
+
+ if( !package ) return depended;
+ depended = 0;
+
+ if( !(list = package->requires->list) ) return depended;
+
+ while( list )
+ {
+ next = dlist_next( list );
+ {
+ int has_extern_dependencies = 0, already_provided = 0;
+
+ struct pkg *pkg = (struct pkg *)list->data;
+ struct dlist *found = dlist_find_data( extern_requires, __compare_required, (const void *)pkg );
+
+ if( found )
+ {
+ if( cmp_version( (const char *)((struct pkg *)found->data)->version, (const char *)pkg->version ) >= 0 )
+ {
+ /* required package is found in the extern_requires list */
+ has_extern_dependencies += 1;
+ }
+ }
+
+ found = dlist_find_data( provides, __compare_provided, (const void *)pkg );
+ if( found )
+ {
+ if( cmp_version( (const char *)((struct package *)found->data)->pkginfo->version, (const char *)pkg->version ) >= 0 )
+ {
+ /* required package is found in the extern_requires list */
+ already_provided += 1;
+ }
+ }
+
+ if( !already_provided && !has_extern_dependencies ) depended += 1;
+ }
+ list = next;
+ }
+
+ /* Check if the package with the same name already exists in the provides list */
+ update = dlist_find_data( provides, __compare_packages_by_name, (const void *)package );
+ if( update )
+ {
+ /* Set install procedure to UPDATE: */
+ package->procedure = UPDATE;
+ }
+
+ return depended;
+}
+/*
+ End of Check REQUIRES functions.
+ ***************************************************************/
+
+static void __fill_provides_list( void *data, void *user_data )
+{
+ struct package *package = (struct package *)data;
+
+ if( package )
+ {
+ if( !check_dependencies( package ) )
+ {
+ /* move independed package to the provides list */
+ packages = dlist_remove( packages, (const void *)data );
+ provides = dlist_append( provides, (void *)package );
+ }
+ }
+}
+
+
+static void __print_extern_package( void *data, void *user_data )
+{
+ FILE *output = (FILE *)user_data;
+ struct pkg *pkg = (struct pkg *)data;
+
+ if( pkg )
+ {
+ if( pkg->group ) { fprintf( output, "# required: %s/%s=%s\n", pkg->group, pkg->name, pkg->version ); }
+ else { fprintf( output, "# required: %s=%s\n", pkg->name, pkg->version ); }
+ }
+}
+
+static void __print_provided_package( void *data, void *user_data )
+{
+ FILE *output = (FILE *)user_data;
+ struct package *package = (struct package *)data;
+
+ if( package )
+ {
+ fprintf( output, "%s:", package->pkginfo->name );
+ fprintf( output, "%s:", package->pkginfo->version );
+ fprintf( output, "%s:", package->pkginfo->short_description );
+ if( package->tarball )
+ {
+ fprintf( output, "%s:", package->tarball );
+ }
+ else
+ {
+ if( package->pkginfo->group ) fprintf( output, "%s/", package->pkginfo->group );
+
+ fprintf( output, "%s-", package->pkginfo->name );
+ fprintf( output, "%s-", package->pkginfo->version );
+ fprintf( output, "%s-", package->pkginfo->arch );
+ fprintf( output, "%s-", package->pkginfo->distro_name );
+ fprintf( output, "%s.", package->pkginfo->distro_version );
+ fprintf( output, "%s:", tarball_suffix ); /* default is '.txz' */
+ }
+ fprintf( output, "%s:", strproc( package->procedure ) );
+ fprintf( output, "%s\n", strprio( package->priority, 0 ) );
+ }
+}
+
+
+static void __reduce_packages_list( struct pkg *pkg )
+{
+ struct package *package = NULL;
+ struct dlist *found = NULL;
+
+ if( !pkg ) return;
+
+ found = dlist_find_data( packages, __compare_provided, (const void *)pkg );
+ if( found && found->data )
+ {
+ struct dlist *list = NULL, *next = NULL;
+
+ package = (struct package *)found->data;
+
+ packages = dlist_remove( packages, (const void *)package );
+ provides = dlist_append( provides, (void *)package );
+
+ if( !(list = package->requires->list) ) return;
+
+ while( list )
+ {
+ next = dlist_next( list );
+ {
+ __reduce_packages_list( (struct pkg *)list->data );
+ }
+ list = next;
+ }
+ }
+}
+
+static void __reduce_packages_list_single( void *data, void *user_data )
+{
+ struct pkg *pkg = (struct pkg *)data;
+
+ if( pkg ) { __reduce_packages_list( pkg ); }
+}
+
+
+static void reduce_packages_list( struct dlist *srcpkgs )
+{
+ if( ! srcpkgs ) return;
+
+ dlist_foreach( srcpkgs, __reduce_packages_list_single, NULL );
+
+ dlist_free( packages, __package_free_func );
+ if( dlist_length( provides ) != 0 )
+ {
+ packages = provides;
+ provides = NULL;
+ }
+}
+
+int create_provides_list( struct dlist *srcpkgs )
+{
+ int ret = 0;
+
+ if( !packages ) return ret;
+
+ if( srcpkgs && dlist_length( srcpkgs ) > 0 )
+ {
+ /******************************************************************
+ Reduce packages list to the list of requires of source packages:
+ */
+ reduce_packages_list( srcpkgs );
+ }
+
+ /* Fill two lists: provides and extern_requires: */
+ dlist_foreach( packages, __fill_extern_requires, NULL );
+
+ /* Remove packages from extern_requires list which present in the provides list: */
+ dlist_foreach( provides, __clean_extern_requires, NULL );
+
+ /* Now we don't need previous contents of provides list: */
+ dlist_free( provides, __pkg_free_func );
+ provides = NULL;
+
+ /* Remove old packages if required new version of them */
+ remove_old_packages();
+
+ /* move packages into provides list in order of installation: */
+ while( dlist_length( packages ) != 0 )
+ {
+ dlist_foreach( packages, __fill_provides_list, NULL );
+ }
+
+ return dlist_length( extern_requires );
+}
+
+void free_provides_list( void )
+{
+ if( htmlroot ) { free( htmlroot ); htmlroot = NULL; }
+ if( hardware ) { free( hardware ); hardware = NULL; }
+
+ dlist_free( extern_requires, __pkg_free_func );
+ dlist_free( provides, __package_free_func );
+}
+
+void print_provides_list( const char *plist_fname )
+{
+ FILE *plist = NULL;
+
+ if( !plist_fname || !provides ) return;
+
+ plist = fopen( plist_fname, "w" );
+ if( !plist )
+ {
+ FATAL_ERROR( "Cannot create output %s file", basename( (char *)plist_fname ) );
+ }
+
+ fprintf( plist, "#\n" );
+ fprintf( plist, "# file format:\n" );
+ fprintf( plist, "# ===========\n" );
+ fprintf( plist, "#\n" );
+ fprintf( plist, "# Each line contains six fields separated by colon symbol ':' like following.\n" );
+ fprintf( plist, "#\n" );
+ fprintf( plist, "# pkgname:version:description:tarball:procedure:priority\n" );
+ fprintf( plist, "#\n" );
+ fprintf( plist, "# where:\n" );
+ fprintf( plist, "#\n" );
+ fprintf( plist, "# pkgname - should be the same as the value of pkgname in the '.DESCRIPTION' file;\n" );
+ fprintf( plist, "# version - package version for showing in check list dialog box if this file is\n" );
+ fprintf( plist, "# used to complete common check dialog for installing group of packages;\n" );
+ fprintf( plist, "# description - short description for showing in check list dialog box if this file is\n" );
+ fprintf( plist, "# used to complete common check dialog for installing group of packages;\n" );
+ fprintf( plist, "# tarball - should end in '.txz';\n" );
+ fprintf( plist, "# procedure - installation procedure {install | update}:\n" );
+ fprintf( plist, "# * 'install' - if package requires normal installation,\n" );
+ fprintf( plist, "# * 'update' - if already installed package should be updated by this\n" );
+ fprintf( plist, "# package archive;\n" );
+ fprintf( plist, "# priority - { REQUIRED|RECOMMENDED|OPTIONAL|SKIP }\n" );
+ fprintf( plist, "# synonims:\n" );
+ fprintf( plist, "# { REQUIRED | required | REQ | req }\n" );
+ fprintf( plist, "# { RECOMMENDED | recommended | REC | rec }\n" );
+ fprintf( plist, "# { OPTIONAL | optional | OPT | opt }\n" );
+ fprintf( plist, "# { SKIP | skip | SKP | skp }\n" );
+ fprintf( plist, "#\n" );
+
+ if( extern_requires )
+ {
+ dlist_foreach( extern_requires, __print_extern_package, plist );
+ fprintf( plist, "#\n" );
+ }
+ dlist_foreach( provides, __print_provided_package, plist );
+
+ fflush( plist );
+ fclose( plist );
+}
+
+
+/***************************************************************
+ Requires TREE functions:
+ */
+
+struct _ctx
+{
+ FILE *output;
+ int index, size, depth;
+};
+
+/**************************
+ HTML Template Variables:
+ */
+static char *root = NULL;
+static char *bug_url = NULL;
+
+static int svg_width = 2;
+static int svg_height = 2;
+
+static char *json_pkgs_file = NULL;
+static char *json_tree_file = NULL;
+
+static char *copying = "Radix cross Linux";
+
+#define max(a,b) ({ typeof (a) _a = (a); typeof (b) _b = (b); _a > _b ? _a : _b; })
+
+/*
+ формирование имен файлов для вывода REQUIRES tree:
+
+ json_fname | last argument of make-pkglist | last argument type
+ -------------------------+-------------------------------+--------------------
+ './a.txt' | a.txt | regular file
+ './a.json' | a.json | regular file
+ './.json' | .json | regular file
+ './khadas-vim.json' | . | directory
+ './tmp/khadas-vim.json' | tmp | directory
+ -------------------------+-------------------------------+--------------------
+
+ - если есть основное базовое имя файла и расширение, то расширение
+ заменяем на: '.pkgs.json', '.tree.json', '.tree.html';
+
+ - если есть основное базовое имя файла без расширения, то добавляем
+ расширение: '.pkgs.json', '.tree.json', '.tree.html';
+
+ - если основное базовое имя файла начинается с точки, то расширение
+ заменяем на: 'pkgs.json', 'tree.json', 'tree.html'.
+*/
+static void allocate_fnames( const char *json_fname )
+{
+ char *p, *e, *f = NULL;
+ char *buf = NULL;
+
+ buf = (char *)malloc( (size_t)PATH_MAX );
+ if( !buf ) { FATAL_ERROR( "Cannot allocate memory" ); }
+ bzero( (void *)buf, PATH_MAX );
+
+ (void)sprintf( &buf[0], "%s", json_fname );
+ p = rindex( (const char *)&buf[0], '/' );
+ if( p )
+ {
+ if( p != &buf[0] ) f = ++p;
+ else f = &buf[0];
+ }
+ e = rindex( (const char *)f, '.' );
+ if( e )
+ {
+ if( e != f )
+ {
+ (void)sprintf( e, ".pkgs.json" ); pkgs_fname = xstrdup( (const char *)&buf[0] );
+ (void)sprintf( e, ".tree.json" ); tree_fname = xstrdup( (const char *)&buf[0] );
+ (void)sprintf( e, ".tree.html" ); html_fname = xstrdup( (const char *)&buf[0] );
+
+ (void)sprintf( e, ".pkgs.min.json" ); pkgs_min_fname = xstrdup( (const char *)&buf[0] );
+ (void)sprintf( e, ".tree.min.json" ); tree_min_fname = xstrdup( (const char *)&buf[0] );
+ }
+ else
+ {
+ (void)sprintf( e, "pkgs.json" ); pkgs_fname = xstrdup( (const char *)&buf[0] );
+ (void)sprintf( e, "tree.json" ); tree_fname = xstrdup( (const char *)&buf[0] );
+ (void)sprintf( e, "tree.html" ); html_fname = xstrdup( (const char *)&buf[0] );
+
+ (void)sprintf( e, "pkgs.min.json" ); pkgs_min_fname = xstrdup( (const char *)&buf[0] );
+ (void)sprintf( e, "tree.min.json" ); tree_min_fname = xstrdup( (const char *)&buf[0] );
+ }
+ }
+ else
+ {
+ e = f + strlen( f );
+
+ (void)sprintf( e, ".pkgs.json" ); pkgs_fname = xstrdup( (const char *)&buf[0] );
+ (void)sprintf( e, ".tree.json" ); tree_fname = xstrdup( (const char *)&buf[0] );
+ (void)sprintf( e, ".tree.html" ); html_fname = xstrdup( (const char *)&buf[0] );
+
+ (void)sprintf( e, ".pkgs.min.json" ); pkgs_min_fname = xstrdup( (const char *)&buf[0] );
+ (void)sprintf( e, ".tree.min.json" ); tree_min_fname = xstrdup( (const char *)&buf[0] );
+ }
+
+ if( minimize )
+ {
+ json_pkgs_file = xstrdup( (const char *)basename( pkgs_min_fname ) );
+ json_tree_file = xstrdup( (const char *)basename( tree_min_fname ) );
+ }
+ else
+ {
+ json_pkgs_file = xstrdup( (const char *)basename( pkgs_fname ) );
+ json_tree_file = xstrdup( (const char *)basename( tree_fname ) );
+ }
+
+ free( buf );
+}
+
+
+/*******************************************************************
+ find_pkg():
+ ----------
+ Returns package found in packages list coresponded to pkg.
+ */
+static struct package *find_pkg( struct dlist *list, struct pkg *pkg )
+{
+ struct package *package = NULL;
+ struct dlist *found = NULL;
+
+ if( !pkg ) return package;
+
+ found = dlist_find_data( list, __compare_provided, (const void *)pkg );
+ if( found )
+ {
+ return (struct package *)found->data;
+ }
+
+ return package;
+}
+
+/*******************************************************************
+ find_package():
+ --------------
+ Returns package found in packages list coresponded to package.
+ */
+static struct package *find_package( struct dlist *list, struct package *pkg )
+{
+ struct package *package = NULL;
+ struct dlist *found = NULL;
+
+ if( !pkg ) return package;
+
+ found = dlist_find_data( list, __compare_packages, (const void *)pkg );
+ if( found )
+ {
+ return (struct package *)found->data;
+ }
+
+ return package;
+}
+
+static void __print_package_data( FILE *output, struct package *package )
+{
+ if( !output || !package ) return;
+
+ /* "id": "net:bind-9.10.1", */
+ if( package->pkginfo->group ) {
+ fprintf( output, " \"id\": \"%s:%s-%s\",\n", package->pkginfo->group,
+ package->pkginfo->name,
+ package->pkginfo->version );
+ } else {
+ fprintf( output, " \"id\": \"%s-%s\",\n", package->pkginfo->name,
+ package->pkginfo->version );
+ }
+ /* "name": "bind", */
+ fprintf( output, " \"name\": \"%s\",\n", package->pkginfo->name );
+ /* "version": "9.10.1", */
+ fprintf( output, " \"version\": \"%s\",\n", package->pkginfo->version );
+ /* "group": "net", */
+ if( package->pkginfo->group ) {
+ fprintf( output, " \"group\": \"%s\",\n", package->pkginfo->group );
+ } else {
+ fprintf( output, " \"group\": \"\",\n" );
+ }
+ /* "arch": "omap543x-eglibc", */
+ fprintf( output, " \"arch\": \"%s\",\n", package->pkginfo->arch );
+ /* "hardware": "omap5uevm", */
+ fprintf( output, " \"hardware\": \"%s\",\n", hardware );
+ /* "license": "custom", */
+ fprintf( output, " \"license\": \"%s\",\n", package->pkginfo->license );
+ /* "description": "bind 9.10.1 (DNS server and utilities)", */
+ fprintf( output, " \"description\": \"%s %s (%s)\",\n", package->pkginfo->name,
+ package->pkginfo->version,
+ package->pkginfo->short_description );
+ /* "uncompressed_size": "17M", */
+ fprintf( output, " \"uncompressed_size\": \"" );
+ if( package->pkginfo->uncompressed_size > 1048576 ) {
+ fprintf( output, "%ldG\",\n", package->pkginfo->uncompressed_size / 1048576 );
+ } else if( package->pkginfo->uncompressed_size > 1024 ) {
+ fprintf( output, "%ldM\",\n", package->pkginfo->uncompressed_size / 1024 );
+ } else {
+ fprintf( output, "%ldK\",\n", package->pkginfo->uncompressed_size );
+ }
+ /* "total_files": "421" */
+ fprintf( output, " \"total_files\": \"%d\"\n", package->pkginfo->total_files );
+}
+
+static void __print_pkgs_node( void *data, void *user_data )
+{
+ struct package *package = (struct package *)data;
+ struct _ctx *ctx = (struct _ctx *)user_data;
+
+ if( !package || !ctx ) return;
+
+ if( ctx->index != 0 )
+ {
+ fprintf( ctx->output, " },\n {\n" );
+ }
+ __print_package_data( ctx->output, package );
+ ++ctx->index;
+}
+
+static void print_pkgs_json( FILE *output, struct dlist *list )
+{
+ struct _ctx ctx;
+
+ if( !output ) return;
+
+ bzero( (void *)&ctx, sizeof(struct _ctx) );
+
+ ctx.output = output;
+ ctx.index = 0;
+
+ fprintf( output, "[{\n" );
+
+ dlist_foreach( list, __print_pkgs_node, (void *)&ctx );
+
+ fprintf( output, " }]\n" );
+}
+
+static void __remove_required_package( void *data, void *user_data )
+{
+ struct package *package = NULL;
+ struct pkg *pkg = (struct pkg *)data;
+
+ if( pkg )
+ {
+ package = find_pkg( tree, pkg );
+ if( package )
+ {
+ /*******************************************
+ if package reqired for some other package
+ we have to remove it from tree list:
+ */
+ tree = dlist_remove_data( tree, __compare_packages, NULL, (const void *)package );
+ }
+ }
+}
+
+static void __remove_required_packages( void *data, void *user_data )
+{
+ struct package *package = (struct package *)data;
+ struct dlist *list = NULL;
+
+ if( !package ) return;
+
+ if( !(list = package->requires->list) ) return;
+
+ dlist_foreach( list, __remove_required_package, NULL );
+}
+
+static void remove_required_packages( struct dlist *list )
+{
+ dlist_foreach( list, __remove_required_packages, NULL );
+}
+
+
+static void __check_pkg_requires( void *data, void *user_data )
+{
+ struct pkg *pkg = (struct pkg *)data;
+ int *counter = (int *)user_data;
+
+ if( pkg )
+ {
+ struct package *package = find_pkg( provides, pkg );
+ if( package ) { ++(*counter); }
+ }
+}
+
+static int check_pkg_requires( struct dlist *list )
+{
+ int cnt = 0;
+ dlist_foreach( list, __check_pkg_requires, (void *)&cnt );
+ return cnt;
+}
+
+
+/***************************************************************
+ Sort Requires Tree functions:
+ ----------------------------
+
+ NOTE:
+ Requires sorted in reverse installation order according to
+ provides list.
+ */
+
+static int __install_pkg_index( struct dlist *list, struct pkg *pkg )
+{
+ int index = -1;
+
+ if( !pkg ) return index;
+
+ if( list )
+ {
+ struct package *package = find_pkg( list, pkg );
+
+ index = dlist_index( list, package );
+ }
+
+ return index;
+}
+
+static int __install_package_index( struct dlist *list, struct package *package )
+{
+ int index = -1;
+
+ if( !package ) return index;
+
+ if( list )
+ index = dlist_index( list, package );
+
+ return index;
+}
+
+static int __compare_pkg_order( const void *a, const void *b )
+{
+ int ret = 0;
+ int ia = -1, ib = -1;
+
+ ia = __install_pkg_index( provides, (struct pkg *)a );
+ ib = __install_pkg_index( provides, (struct pkg *)b );
+
+ if( ia < ib ) ret = -1;
+ if( ia > ib ) ret = 1;
+
+ return ret;
+}
+
+static int __compare_package_order( const void *a, const void *b )
+{
+ int ret = 0;
+ int ia = -1, ib = -1;
+
+ ia = __install_package_index( provides, (struct package *)a );
+ ib = __install_package_index( provides, (struct package *)b );
+
+ if( ia < ib ) ret = -1;
+ if( ia > ib ) ret = 1;
+
+ return ret;
+}
+
+static void __sort_requires( void *data, void *user_data )
+{
+ struct package *package = (struct package *)data;
+ struct dlist *reqs = NULL;
+
+ if( !package ) return;
+
+ if( (reqs = package->requires->list) && check_pkg_requires( reqs ) > 0 )
+ {
+ package->requires->list = reqs = dlist_sort( reqs, __compare_pkg_order );
+ }
+}
+
+static struct dlist *sort_requires_tree( struct dlist *list )
+{
+ int lenght = 0;
+
+ if( !list ) return list;
+
+ lenght = dlist_length( list );
+
+ if( lenght > 1 )
+ list = dlist_sort( list, __compare_package_order );
+
+ return list;
+}
+
+static void sort_requires( struct dlist *list )
+{
+ int lenght = 0;
+
+ if( !list ) return;
+
+ dlist_foreach( list, __sort_requires, NULL );
+}
+/*
+ End of Sort Requires Tree functions:
+ ***************************************************************/
+
+/***************************************************************
+ Binary Tree functions:
+ ---------------------
+ */
+static struct dlist *pkgs = NULL;
+static struct btree *btree = NULL;
+
+static struct pkg *duplicate_pkg( struct package *package )
+{
+ struct pkg *pkg = NULL;
+
+ if( !package ) return pkg;
+
+ pkg = pkg_alloc();
+ pkg->name = xstrdup( (const char *)package->pkginfo->name );
+ pkg->group = xstrdup( (const char *)package->pkginfo->group );
+ pkg->version = xstrdup( (const char *)package->pkginfo->version );
+ pkg->procedure = package->procedure;
+
+ return pkg;
+}
+
+static void __fill_pkgs_list( void *data, void *user_data )
+{
+ struct package *package = (struct package *)data;
+ struct pkg *pkg = NULL;
+
+ if( !package ) return;
+
+ pkg = duplicate_pkg( package );
+ pkgs = dlist_append( pkgs, (void *)pkg );
+}
+
+/*******************************************
+ create_pkgs_list():
+ ------------------
+ Creates pkgs list from provides list.
+ */
+static void create_pkgs_list( struct dlist *list )
+{
+ if( !list ) return;
+
+ dlist_foreach( list, __fill_pkgs_list, NULL );
+}
+
+static void free_pkgs_list()
+{
+ if( pkgs )
+ {
+ dlist_free( pkgs, __pkg_free_func );
+ pkgs = NULL;
+ }
+}
+
+static int __compare_pkg( const void *a, const void *b )
+{
+ int ret = -1;
+
+ struct pkg *pkg1 = (struct pkg *)a;
+ struct pkg *pkg2 = (struct pkg *)b;
+
+ if( pkg1->group && pkg2->group )
+ {
+ ret = strcmp( pkg1->group, pkg2->group );
+ }
+ else if( !pkg1->group && !pkg2->group )
+ {
+ ret = 0;
+ }
+ else if( pkg1->group )
+ {
+ ret = 1;
+ }
+
+ if( ! ret )
+ {
+ return strcmp( pkg1->name, pkg2->name );
+ }
+ return ret;
+}
+
+static struct pkg *__find_pkg( struct dlist *list, struct pkg *package )
+{
+ struct pkg *pkg = NULL;
+ struct dlist *found = NULL;
+
+ if( !package ) return pkg;
+
+ found = dlist_find_data( list, __compare_pkg, (const void *)package );
+ if( found )
+ {
+ return (struct pkg *)found->data;
+ }
+
+ return pkg;
+}
+
+static int __compare_pkg_with_package( const void *a, const void *b )
+{
+ int ret = -1;
+
+ struct pkg *pkg1 = (struct pkg *)a;
+ struct package *pkg2 = (struct package *)b;
+
+ if( pkg1->group && pkg2->pkginfo->group )
+ {
+ ret = strcmp( pkg1->group, pkg2->pkginfo->group );
+ }
+ else if( !pkg1->group && !pkg2->pkginfo->group )
+ {
+ ret = 0;
+ }
+ else if( pkg2->pkginfo->group )
+ {
+ ret = 1;
+ }
+
+ if( ! ret )
+ {
+ return strcmp( pkg1->name, pkg2->pkginfo->name );
+ }
+ return ret;
+}
+
+static struct pkg *__find_pkg_by_package( struct dlist *list, struct package *package )
+{
+ struct pkg *pkg = NULL;
+ struct dlist *found = NULL;
+
+ if( !package ) return pkg;
+
+ found = dlist_find_data( list, __compare_pkg_with_package, (const void *)package );
+ if( found )
+ {
+ return (struct pkg *)found->data;
+ }
+
+ return pkg;
+}
+
+static struct pkg *__find_pkg_by_pkg( struct dlist *list, struct pkg *package )
+{
+ struct pkg *pkg = NULL;
+ struct dlist *found = NULL;
+
+ if( !package ) return pkg;
+
+ found = dlist_find_data( list, __compare_pkg, (const void *)package );
+ if( found )
+ {
+ return (struct pkg *)found->data;
+ }
+
+ return pkg;
+}
+
+static void __btree_add_requires( struct btree *tree );
+
+static struct btree *__btree_add_left( void *data, void *user_data )
+{
+ struct btree *tree = (struct btree *)user_data;
+ struct pkg *left = (struct pkg *)data;
+ struct pkg *pkg = NULL;
+ struct btree *node = NULL;
+
+ if( !tree || !left ) return node;
+
+ pkg = __find_pkg_by_pkg( pkgs, left );
+
+ node = btree_insert_left( tree, __btree_alloc( (void *)pkg ) );
+ __btree_add_requires( node );
+
+ return node;
+}
+
+static void __btree_add_right( void *data, void *user_data )
+{
+ struct btree *tree = *((struct btree **)user_data);
+ struct pkg *right = (struct pkg *)data;
+ struct pkg *pkg = NULL;
+ struct btree *node = NULL;
+
+ if( !tree || !right ) return;
+
+ pkg = __find_pkg_by_pkg( pkgs, right );
+
+ node = btree_insert_right( tree, __btree_alloc( (void *)pkg ) );
+ __btree_add_requires( node );
+
+ *((struct btree **)user_data) = node;
+}
+
+static void __btree_add_requires( struct btree *tree )
+{
+ struct package *package = NULL;
+
+ if( !tree ) return;
+
+ package = find_pkg( provides, (struct pkg *)tree->data );
+ if( package )
+ {
+ struct dlist *list = NULL;
+
+ if( (list = package->requires->list) )
+ {
+ struct pkg *pkg = NULL;
+ struct btree *node = NULL;
+
+ pkg = __find_pkg_by_pkg( pkgs, (struct pkg *)list->data );
+
+ node = __btree_add_left( (void *)pkg, (void *)tree );
+
+ if( dlist_length( list ) > 1 )
+ {
+ dlist_foreach( list->next, __btree_add_right, (void *)&node );
+ }
+ }
+ }
+}
+
+static void __fill_btree( void *data, void *user_data )
+{
+ struct btree *tree = *((struct btree **)user_data);
+ struct package *package = (struct package *)data;
+ struct pkg *pkg = NULL;
+ struct btree *node = NULL;
+
+ if( !tree || !package ) return;
+
+ pkg = __find_pkg_by_package( pkgs, package );
+
+ node = btree_insert_right( tree, __btree_alloc( (void *)pkg ) );
+ __btree_add_requires( node );
+
+ *((struct btree **)user_data) = node;
+}
+
+/*******************************************************************
+ __print_btree_pkg():
+ -------------------
+ Print out package "group/name-version". Used for debuging only.
+ */
+static void __print_btree_pkg( void *data, void *user_data )
+{
+ struct pkg *pkg = (struct pkg *)data;
+
+ if( !pkg ) return;
+
+ fprintf( stdout, "%s/%s-%s\n", pkg->group, pkg->name, pkg->version );
+}
+
+/*****************************************************************
+ __print_btree_node():
+ --------------------
+ Print out package in JSON format. Used by btree_print_json().
+ */
+static void __print_btree_node( void *data, void *user_data )
+{
+ struct pkg *pkg = (struct pkg *)data;
+ struct _bctx *ctx = (struct _bctx *)user_data;
+
+ char *p, buf[PATH_MAX*2];
+ int depth = 0, max_depth = PATH_MAX + PATH_MAX / 2;
+
+ if( !pkg || !ctx ) return;
+
+ buf[0] = ' ';
+ buf[1] = '\0';
+
+ p = (char *)&buf[1];
+ depth = ctx->indent;
+
+ if( depth < 1 ) depth = 0;
+ if( depth > max_depth ) depth = max_depth;
+
+ while( depth )
+ {
+ (void)sprintf( p, " " ); --depth; ++p; *p = '\0';
+ }
+
+ if( pkg->group )
+ (void)sprintf( p, "\"name\": \"%s:%s-%s\"", pkg->group,
+ pkg->name,
+ pkg->version );
+ else
+ (void)sprintf( p, "\"name\": \"%s-%s\"", pkg->name,
+ pkg->version );
+
+ fprintf( ctx->output, (char *)&buf[0] );
+}
+
+static int __width_factor( int width )
+{
+ double w, x = (double)width;
+
+ if( width < 2 ) return 48;
+
+ if( width > 1 && width < 150 )
+ {
+ w = floor( (38.399 - 7.4262 * log( x )) + 8.0 );
+
+ return (int)w;
+ }
+ else
+ {
+ return 8;
+ }
+}
+
+/*******************************************************************
+ __print_btree_header():
+ ----------------------
+ Print out Binary Tree Header.
+ */
+static void __print_btree_header( FILE *fp, struct btree *btree, struct dlist *tree )
+{
+ struct package *package = NULL;
+
+ if( !fp || !btree || !tree ) return;
+
+ package = find_pkg( provides, (struct pkg *)btree->data );
+
+ if( package )
+ {
+ char *buf = NULL;
+
+ if( dlist_length( tree ) > 1 )
+ {
+ buf = (char *)malloc( (size_t)PATH_MAX );
+ if( !buf ) { FATAL_ERROR( "Cannot allocate memory" ); }
+ bzero( (void *)buf, PATH_MAX );
+
+ (void)sprintf( &buf[0], "%s", htmlroot );
+ root = xstrdup( (const char *)&buf[0] );
+ (void)sprintf( &buf[0], "%s", package->pkginfo->url );
+ bug_url = xstrdup( (const char *)&buf[0] );
+ free( buf );
+ }
+ else
+ {
+ buf = (char *)malloc( (size_t)PATH_MAX );
+ if( !buf ) { FATAL_ERROR( "Cannot allocate memory" ); }
+ bzero( (void *)buf, PATH_MAX );
+
+ if( package->pkginfo->group )
+ (void)sprintf( &buf[0], "%s/%s-%s", package->pkginfo->group,
+ package->pkginfo->name,
+ package->pkginfo->version );
+ else
+ (void)sprintf( &buf[0], "%s-%s", package->pkginfo->name,
+ package->pkginfo->version );
+
+ root = xstrdup( (const char *)&buf[0] );
+ (void)sprintf( &buf[0], "%s", package->pkginfo->url );
+ bug_url = xstrdup( (const char *)&buf[0] );
+ free( buf );
+ }
+
+ fprintf( fp, " \"distro\": [ \"%s\", \"%s\", \"%s\" ],\n",
+ package->pkginfo->distro_name,
+ package->pkginfo->distro_version,
+ package->pkginfo->url );
+ }
+}
+
+
+/***************************************
+ create_btree():
+ --------------
+ Creates btree from tree list (DAG).
+ */
+static void create_btree( struct dlist *dag, struct dlist *install )
+{
+ struct pkg *pkg = NULL;
+ struct package *package = NULL;
+ struct dlist *list = NULL;
+ struct btree *node = NULL;
+
+ if( !dag || !install ) return;
+
+ /* first package: */
+ package = (struct package *)dag->data;
+ pkg = __find_pkg_by_package( pkgs, package );
+
+ node = btree = __btree_alloc( (void *)pkg );
+
+ __btree_add_requires( node );
+
+ if( dlist_length( dag ) > 1 )
+ dlist_foreach( dag->next, __fill_btree, (void *)&node );
+
+ btree_reduce( btree, __compare_pkg, NULL );
+}
+/*
+ End of Binary Tree functions:
+ ***************************************************************/
+
+
+/***************************************************************
+ Print json format of DAG functions:
+ */
+static void __print_pkg_tree( struct _ctx *ctx, struct dlist *list )
+{
+ struct dlist *next = NULL;
+
+ if( !ctx || !list ) return;
+
+ ctx->depth += 2;
+ svg_width = max( svg_width, ctx->depth );
+
+ while( list )
+ {
+ next = dlist_next( list );
+ {
+ struct pkg *pkg = (struct pkg *)list->data;
+ struct package *package = find_pkg( provides, pkg );
+
+ if( package )
+ {
+ char *p, *buf = NULL;
+ int depth = 0;
+
+ struct dlist *reqs = NULL;
+
+ buf = (char *)malloc( (size_t)PATH_MAX );
+ if( !buf ) { FATAL_ERROR( "Cannot allocate memory" ); }
+ bzero( (void *)buf, PATH_MAX );
+
+ buf[0] = ' ';
+ buf[1] = '\0';
+
+ p = (char *)&buf[1];
+ depth = ctx->depth;
+
+ while( depth ) { (void)sprintf( p, " " ); --depth; ++p; *p = '\0'; }
+
+ (void)sprintf( p - 1, "{\n" );
+ fprintf( ctx->output, (char *)&buf[0] );
+ *(p - 1) = ' '; *p = '\0';
+
+ if( pkg->group )
+ (void)sprintf( p, "\"name\": \"%s:%s-%s\"", pkg->group,
+ pkg->name,
+ pkg->version );
+ else
+ (void)sprintf( p, "\"name\": \"%s-%s\"", pkg->name,
+ pkg->version );
+
+ fprintf( ctx->output, (char *)&buf[0] );
+
+ if( (reqs = package->requires->list) && check_pkg_requires( reqs ) > 0 )
+ {
+ fprintf( ctx->output, ",\n" );
+
+ (void)sprintf( p, "\"children\": [\n" );
+ fprintf( ctx->output, (char *)&buf[0] );
+
+ __print_pkg_tree( ctx, reqs );
+
+ (void)sprintf( p, "]\n" );
+ fprintf( ctx->output, (char *)&buf[0] );
+ }
+ else
+ {
+ fprintf( ctx->output, "\n" );
+ }
+
+ (void)sprintf( p - 1, "}" );
+ fprintf( ctx->output, (char *)&buf[0] );
+ *(p - 1) = ' '; *p = '\0';
+
+ if( next ) { fprintf( ctx->output, ",\n" ); }
+ else { fprintf( ctx->output, "\n" ); }
+
+ free( buf );
+ } /* End if( package ) */
+ }
+ list = next;
+ } /* End of while( list ) */
+
+ ctx->depth -= 2;
+}
+
+static void __print_package_node( struct _ctx *ctx, struct package *package )
+{
+ char *p, *buf = NULL;
+ int depth = 0;
+
+ struct dlist *list = NULL;
+
+ if( !package || !ctx ) return;
+
+ buf = (char *)malloc( (size_t)PATH_MAX );
+ if( !buf ) { FATAL_ERROR( "Cannot allocate memory" ); }
+ bzero( (void *)buf, PATH_MAX );
+
+ buf[0] = ' ';
+ buf[1] = '\0';
+
+ p = (char *)&buf[1];
+ depth = ctx->depth;
+
+ while( depth ) { (void)sprintf( p, " " ); --depth; ++p; *p = '\0'; }
+
+ (void)sprintf( p - 1, "{\n" );
+ fprintf( ctx->output, (char *)&buf[0] );
+ *(p - 1) = ' '; *p = '\0';
+
+ if( package->pkginfo->group )
+ (void)sprintf( p, "\"name\": \"%s:%s-%s\"", package->pkginfo->group,
+ package->pkginfo->name,
+ package->pkginfo->version );
+ else
+ (void)sprintf( p, "\"name\": \"%s-%s\"", package->pkginfo->name,
+ package->pkginfo->version );
+
+ fprintf( ctx->output, (char *)&buf[0] );
+
+ if( (list = package->requires->list) && check_pkg_requires( list ) > 0 )
+ {
+ fprintf( ctx->output, ",\n" );
+
+ (void)sprintf( p, "\"children\": [\n" );
+ fprintf( ctx->output, (char *)&buf[0] );
+
+ __print_pkg_tree( ctx, list );
+
+ (void)sprintf( p, "]\n" );
+ fprintf( ctx->output, (char *)&buf[0] );
+ }
+ else
+ {
+ fprintf( ctx->output, "\n" );
+ }
+
+ (void)sprintf( p - 1, "}" );
+ fprintf( ctx->output, (char *)&buf[0] );
+ *(p - 1) = ' '; *p = '\0';
+
+ free( buf );
+}
+
+static void __print_tree_node( void *data, void *user_data )
+{
+ struct package *package = (struct package *)data;
+ struct _ctx *ctx = (struct _ctx *)user_data;
+
+ if( !package || !ctx ) return;
+
+ if( ctx->size > 1 )
+ {
+ if( ctx->index == 0 )
+ {
+ char *buf = NULL;
+
+ buf = (char *)malloc( (size_t)PATH_MAX );
+ if( !buf ) { FATAL_ERROR( "Cannot allocate memory" ); }
+ bzero( (void *)buf, PATH_MAX );
+
+ (void)sprintf( &buf[0], "%s", htmlroot );
+ root = xstrdup( (const char *)&buf[0] );
+ (void)sprintf( &buf[0], "%s", package->pkginfo->url );
+ bug_url = xstrdup( (const char *)&buf[0] );
+ free( buf );
+
+ fprintf( ctx->output, " \"distro\": [ \"%s\", \"%s\", \"%s\" ],\n",
+ package->pkginfo->distro_name,
+ package->pkginfo->distro_version,
+ package->pkginfo->url );
+ fprintf( ctx->output, " \"name\": \"%s\",\n", htmlroot );
+ fprintf( ctx->output, " \"children\": [\n" );
+ }
+
+
+ __print_package_node( ctx, package );
+ svg_height += 2;
+
+
+ if( ctx->index < ctx->size - 1 ) fprintf( ctx->output, "," );
+ else fprintf( ctx->output, "\n ]" );
+
+ fprintf( ctx->output, "\n" );
+ }
+ else
+ {
+ struct dlist *reqs = NULL;
+ char *buf = NULL;
+
+ buf = (char *)malloc( (size_t)PATH_MAX );
+ if( !buf ) { FATAL_ERROR( "Cannot allocate memory" ); }
+ bzero( (void *)buf, PATH_MAX );
+
+ if( package->pkginfo->group )
+ (void)sprintf( &buf[0], "%s/%s-%s", package->pkginfo->group,
+ package->pkginfo->name,
+ package->pkginfo->version );
+ else
+ (void)sprintf( &buf[0], "%s-%s", package->pkginfo->name,
+ package->pkginfo->version );
+
+ root = xstrdup( (const char *)&buf[0] );
+ (void)sprintf( &buf[0], "%s", package->pkginfo->url );
+ bug_url = xstrdup( (const char *)&buf[0] );
+ free( buf );
+
+ fprintf( ctx->output, " \"distro\": [ \"%s\", \"%s\", \"%s\" ],\n",
+ package->pkginfo->distro_name,
+ package->pkginfo->distro_version,
+ package->pkginfo->url );
+ if( package->pkginfo->group )
+ fprintf( ctx->output, " \"name\": \"%s:%s-%s\"", package->pkginfo->group,
+ package->pkginfo->name,
+ package->pkginfo->version );
+ else
+ fprintf( ctx->output, " \"name\": \"%s-%s\"", package->pkginfo->name,
+ package->pkginfo->version );
+
+
+ svg_height += 2;
+ ctx->depth -=2;
+
+ if( (reqs = package->requires->list) && check_pkg_requires( reqs ) > 0 )
+ {
+ fprintf( ctx->output, ",\n" );
+
+ fprintf( ctx->output, " \"children\": [\n" );
+
+ __print_pkg_tree( ctx, reqs );
+
+ fprintf( ctx->output, " ]\n" );
+ }
+
+ }
+
+ ++ctx->index;
+}
+
+static void print_tree_json( FILE *output, struct dlist *list )
+{
+ struct _ctx ctx;
+
+ if( !output || !list ) return;
+
+ bzero( (void *)&ctx, sizeof(struct _ctx) );
+
+ ctx.output = output;
+ ctx.index = 0;
+ ctx.size = dlist_length( list );
+ ctx.depth = 2;
+
+ fprintf( output, "{\n" );
+ dlist_foreach( list, __print_tree_node, (void *)&ctx );
+ fprintf( output, "}\n" );
+
+ svg_height += svg_width / 2;
+
+ svg_width = (svg_width + 4) * 160;
+ svg_height = (svg_height + 4) * 24;
+}
+/*
+ End of print json format of DAG functions.
+ ***************************************************************/
+
+#include <pkglist.html.c>
+
+void print_provides_tree( const char *json_fname, enum _tree_format tree_format )
+{
+ FILE *pkgs_fp = NULL, *tree_fp = NULL, *html_fp = NULL;
+
+ allocate_fnames( json_fname );
+
+ pkgs_fp = fopen( (const char *)pkgs_fname, "w" );
+ if( !pkgs_fp ) { FATAL_ERROR( "Cannot create %s file", basename( pkgs_fname ) ); }
+ tree_fp = fopen( (const char *)tree_fname, "w" );
+ if( !tree_fp ) { FATAL_ERROR( "Cannot create %s file", basename( tree_fname ) ); }
+ html_fp = fopen( (const char *)html_fname, "w" );
+ if( !html_fp ) { FATAL_ERROR( "Cannot create %s file", basename( html_fname ) ); }
+
+ tree = dlist_copy( provides );
+
+ /*****************************************************
+ print out the array of all packages in JSON format:
+ */
+ print_pkgs_json( pkgs_fp, provides );
+ fflush( pkgs_fp ); fclose( pkgs_fp );
+
+ provides = dlist_reverse( provides );
+ sort_requires( provides ); /* sort requires in reverse installation order */
+
+ /********************************************************
+ Sort the REQUIRES TREE in reverse installation order.
+ */
+ tree = sort_requires_tree( tree );
+
+ /********************************************************
+ remove unneded packages from tree list to leave the
+ last installation layer of packages presented in DAG:
+ */
+ remove_required_packages( provides );
+
+
+ if( tree_format == TFMT_BIN )
+ {
+ int width = 0, height = 0;
+
+ /********************************************************************
+ print out the REQUIRES TREE in JSON format as reduced binary tree:
+ */
+ create_pkgs_list( provides );
+ create_btree( tree, provides );
+
+ width = btree_width( btree );
+ height = btree_height( btree );
+
+ svg_width = (height + 4) * 240;
+ svg_height = (width + 4) * __width_factor( width );
+
+ fprintf( tree_fp, "{\n" );
+ __print_btree_header( tree_fp, btree, tree );
+ btree_print_json( tree_fp, btree, __print_btree_node );
+ fprintf( tree_fp, "}\n" );
+
+ __btree_free( btree );
+ free_pkgs_list();
+ }
+ else
+ {
+ /****************************************************
+ print out the REQUIRES TREE in JSON format as DAG:
+ */
+ print_tree_json( tree_fp, tree );
+ }
+
+ fflush( tree_fp ); fclose( tree_fp );
+
+ if( minimize )
+ {
+ if( minimize_json( (const char *)pkgs_fname, (const char *)pkgs_min_fname ) < 1 )
+ {
+ (void)unlink( (const char *)pkgs_min_fname );
+ }
+ if( minimize_json( (const char *)tree_fname, (const char *)tree_min_fname ) < 1 )
+ {
+ (void)unlink( (const char *)tree_min_fname );
+ }
+ }
+
+
+ /***********************************************
+ print out the HTML to view REQIIRES TREE:
+ */
+ print_tree_html( html_fp );
+ fflush( html_fp ); fclose( html_fp );
+
+
+ /*****************
+ free resources:
+ */
+ if( root ) { free( root ); root = NULL; }
+ if( bug_url ) { free( bug_url ); bug_url = NULL; }
+
+ if( pkgs_fname ) { free( pkgs_fname ); pkgs_fname = NULL; }
+ if( tree_fname ) { free( tree_fname ); tree_fname = NULL; }
+ if( html_fname ) { free( html_fname ); html_fname = NULL; }
+
+ if( pkgs_min_fname ) { free( pkgs_min_fname ); pkgs_min_fname = NULL; }
+ if( tree_min_fname ) { free( tree_min_fname ); tree_min_fname = NULL; }
+
+ if( json_pkgs_file ) { free( json_pkgs_file ); json_pkgs_file = NULL; }
+ if( json_tree_file ) { free( json_tree_file ); json_tree_file = NULL; }
+
+ __dlist_free( tree ); /* do not free node data */
+}
+/*
+ End of Requires TREE functions.
+ ***************************************************************/