diff options
Diffstat (limited to 'src/pkglist.c')
-rw-r--r-- | src/pkglist.c | 2198 |
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. + ***************************************************************/ |