diff options
| author | kx <kx@radix-linux.su> | 2026-10-09 18:00:15 +0300 |
|---|---|---|
| committer | kx <kx@radix-linux.su> | 2026-10-09 18:00:15 +0300 |
| commit | 065da3509019b15dd0e678072e6be629e5330bef (patch) | |
| tree | 69c934c9e786ac048b4fbe8f4c4c5571661521f4 /utils/mcpu-ar.c | |
| parent | d130fe27fa753be02ed76fd9b277aa3c3d928eb9 (diff) | |
| download | libmcpu-0.4.0.tar.xz | |
Version 0.4.00.4.0
Diffstat (limited to 'utils/mcpu-ar.c')
| -rw-r--r-- | utils/mcpu-ar.c | 542 |
1 files changed, 542 insertions, 0 deletions
diff --git a/utils/mcpu-ar.c b/utils/mcpu-ar.c new file mode 100644 index 0000000..c3fe4bf --- /dev/null +++ b/utils/mcpu-ar.c @@ -0,0 +1,542 @@ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> + +#include "mcpu-archive.h" +#include "mcpu-version.h" + + +typedef struct mcpu_ar_options +{ + char operation; + int create; + int index; + int verbose; +} mcpu_ar_options; + + +static void +usage( FILE *fp, const char *name ) +{ + fprintf( fp, + "usage: %s [options] operation archive [member ...]\n\n" + "Create and maintain deterministic UNIX archives of MCPU MELF ET_REL objects.\n\n" + "Operations:\n" + " r replace existing members or append new members\n" + " q append members without replacement\n" + " d delete named members\n" + " t list archive members\n" + " p print member contents to standard output\n" + " x extract members into the current directory\n" + " s rebuild the archive symbol index\n\n" + "Modifiers:\n" + " c accepted for ar compatibility; archive creation is quiet\n" + " s write/rebuild the standard archive symbol index\n" + " v verbose operation\n\n" + "The operation and modifiers may be combined, for example: rcs, tv, xv.\n" + "Member order is preserved. r replaces a matching member in place and\n" + "appends a member that is not already present. q always appends.\n\n" + "Options:\n" + " --help - show this help\n" + " --version - show program version\n" + " --dump-version - show version number only\n", + name ); + +} /* End of usage() */ + + +static int +parse_operation( const char *text, mcpu_ar_options *options ) +{ + const char *p = text; + int primary = 0; + + memset( options, 0, sizeof( *options ) ); + if( *p == '-' ) + ++p; + if( !*p ) + return( -1 ); + + while( *p ) + { + switch( *p ) + { + case 'r': case 'q': case 'd': case 't': case 'p': case 'x': + if( primary && options->operation != *p ) + return( -1 ); + options->operation = *p; + primary = 1; + break; + case 's': + options->index = 1; + break; + case 'c': + options->create = 1; + break; + case 'v': + options->verbose = 1; + break; + case 'D': + break; /* Deterministic mode is always active. */ + default: + return( -1 ); + } + ++p; + } + + if( !primary ) + { + if( options->index ) + options->operation = 's'; + else + return( -1 ); + } + return( 0 ); + +} /* End of parse_operation() */ + + +static int +archive_exists( const char *path ) +{ + struct stat st; + return( stat( path, &st ) == 0 ); + +} /* End of archive_exists() */ + + +static int +selector_name( const char *arg, const char **name ) +{ + const char *p = mcpu_archive_basename( arg ); + + if( !p || !*p ) + return( -1 ); + *name = p; + return( 0 ); + +} /* End of selector_name() */ + + +static int +name_selected( const char *name, int argc, char **argv, int *found ) +{ + int i; + + if( argc == 0 ) + return( 1 ); + + for( i = 0; i < argc; ++i ) + { + const char *selector; + + if( selector_name( argv[i], &selector ) ) + continue; + if( !strcmp( name, selector ) ) + { + if( found ) + found[i] = 1; + return( 1 ); + } + } + return( 0 ); + +} /* End of name_selected() */ + + +static int +check_all_found( int argc, char **argv, int *found, const char *prog ) +{ + int i; + int rc = 0; + + for( i = 0; i < argc; ++i ) + if( !found[i] ) + { + fprintf( stderr, "%s: member not found: %s\n", prog, argv[i] ); + rc = -1; + } + return( rc ); + +} /* End of check_all_found() */ + + +static int +replace_member( mcpu_archive *archive, mcpu_archive_member *member, + int verbose ) +{ + size_t i; + + for( i = 0; i < archive->member_count; ++i ) + if( !strcmp( archive->members[i].name, member->name ) ) + { + if( verbose ) + fprintf( stderr, "r - %s\n", member->name ); + mcpu_archive_member_free( &archive->members[i] ); + archive->members[i] = *member; + memset( member, 0, sizeof( *member ) ); + return( 0 ); + } + return( 1 ); + +} /* End of replace_member() */ + + +static int +operation_add( mcpu_archive *archive, char operation, + int argc, char **argv, int verbose, + const char *prog, char *err, size_t errsz ) +{ + int i; + + if( argc == 0 ) + { + fprintf( stderr, "%s: operation %c requires at least one member\n", + prog, operation ); + return( -1 ); + } + + for( i = 0; i < argc; ++i ) + { + mcpu_archive_member member; + int rr = 1; + + memset( &member, 0, sizeof( member ) ); + if( mcpu_archive_member_load( argv[i], &member, err, errsz ) ) + { + fprintf( stderr, "%s: %s: %s\n", prog, argv[i], err ); + return( -1 ); + } + + if( operation == 'r' ) + rr = replace_member( archive, &member, verbose ); + if( rr > 0 ) + { + if( verbose ) + fprintf( stderr, "%c - %s\n", operation, member.name ); + if( mcpu_archive_append( archive, &member, err, errsz ) ) + { + fprintf( stderr, "%s: %s\n", prog, err ); + mcpu_archive_member_free( &member ); + return( -1 ); + } + } + } + return( 0 ); + +} /* End of operation_add() */ + + +static int +operation_delete( mcpu_archive *archive, int argc, char **argv, + int verbose, const char *prog ) +{ + int *found; + size_t i, out; + + if( argc == 0 ) + { + fprintf( stderr, "%s: operation d requires at least one member\n", prog ); + return( -1 ); + } + found = (int *)calloc( (size_t)argc, sizeof( *found ) ); + if( !found ) + { + fprintf( stderr, "%s: out of memory\n", prog ); + return( -1 ); + } + + out = 0; + for( i = 0; i < archive->member_count; ++i ) + { + if( name_selected( archive->members[i].name, argc, argv, found ) ) + { + if( verbose ) + fprintf( stderr, "d - %s\n", archive->members[i].name ); + mcpu_archive_member_free( &archive->members[i] ); + continue; + } + if( out != i ) + { + archive->members[out] = archive->members[i]; + memset( &archive->members[i], 0, sizeof( archive->members[i] ) ); + } + ++out; + } + archive->member_count = out; + + i = check_all_found( argc, argv, found, prog ) ? 1 : 0; + free( found ); + return( i ? -1 : 0 ); + +} /* End of operation_delete() */ + + +static int +operation_list( const mcpu_archive *archive, int argc, char **argv, + int verbose, const char *prog ) +{ + int *found = NULL; + size_t i; + int rc; + + if( argc ) + { + found = (int *)calloc( (size_t)argc, sizeof( *found ) ); + if( !found ) + { + fprintf( stderr, "%s: out of memory\n", prog ); + return( -1 ); + } + } + + for( i = 0; i < archive->member_count; ++i ) + if( name_selected( archive->members[i].name, argc, argv, found ) ) + { + if( verbose ) + printf( "rw-r--r-- 0/0 %10zu Jan 1 00:00 1970 %s\n", + archive->members[i].size, archive->members[i].name ); + else + printf( "%s\n", archive->members[i].name ); + } + + rc = check_all_found( argc, argv, found, prog ); + free( found ); + return( rc ); + +} /* End of operation_list() */ + + +static int +operation_print( const mcpu_archive *archive, int argc, char **argv, + int verbose, const char *prog ) +{ + int *found = NULL; + size_t i; + int rc = 0; + + if( argc ) + { + found = (int *)calloc( (size_t)argc, sizeof( *found ) ); + if( !found ) + { + fprintf( stderr, "%s: out of memory\n", prog ); + return( -1 ); + } + } + + for( i = 0; i < archive->member_count; ++i ) + if( name_selected( archive->members[i].name, argc, argv, found ) ) + { + if( verbose ) + fprintf( stderr, "p - %s\n", archive->members[i].name ); + if( archive->members[i].size && + fwrite( archive->members[i].data, 1, archive->members[i].size, + stdout ) != archive->members[i].size ) + { + fprintf( stderr, "%s: cannot write stdout: %s\n", + prog, strerror( errno ) ); + rc = -1; + break; + } + } + + if( !rc && check_all_found( argc, argv, found, prog ) ) + rc = -1; + free( found ); + return( rc ); + +} /* End of operation_print() */ + + +static int +operation_extract( const mcpu_archive *archive, int argc, char **argv, + int verbose, const char *prog ) +{ + int *found = NULL; + size_t i; + int rc = 0; + + if( argc ) + { + found = (int *)calloc( (size_t)argc, sizeof( *found ) ); + if( !found ) + { + fprintf( stderr, "%s: out of memory\n", prog ); + return( -1 ); + } + } + + for( i = 0; i < archive->member_count; ++i ) + if( name_selected( archive->members[i].name, argc, argv, found ) ) + { + FILE *fp; + + if( verbose ) + fprintf( stderr, "x - %s\n", archive->members[i].name ); + fp = fopen( archive->members[i].name, "wb" ); + if( !fp ) + { + fprintf( stderr, "%s: cannot create %s: %s\n", prog, + archive->members[i].name, strerror( errno ) ); + rc = -1; + break; + } + if( archive->members[i].size && + fwrite( archive->members[i].data, 1, archive->members[i].size, + fp ) != archive->members[i].size ) + { + fprintf( stderr, "%s: cannot write %s: %s\n", prog, + archive->members[i].name, strerror( errno ) ); + fclose( fp ); + rc = -1; + break; + } + if( fclose( fp ) ) + { + fprintf( stderr, "%s: cannot close %s: %s\n", prog, + archive->members[i].name, strerror( errno ) ); + rc = -1; + break; + } + chmod( archive->members[i].name, 0644 ); + } + + if( !rc && check_all_found( argc, argv, found, prog ) ) + rc = -1; + free( found ); + return( rc ); + +} /* End of operation_extract() */ + + +int +main( int argc, char **argv ) +{ + const char *prog = argc > 0 ? argv[0] : "mcpu-ar"; + mcpu_ar_options options; + const char *archive_path; + mcpu_archive archive; + char err[512]; + int exists; + int member_argc; + char **member_argv; + int mutation = 0; + int rc = EXIT_FAILURE; + + if( argc == 2 && !strcmp( argv[1], "--help" ) ) + { + usage( stdout, prog ); + return( EXIT_SUCCESS ); + } + if( argc >= 2 && + (!strcmp( argv[1], "--version" ) || + !strcmp( argv[1], "--dump-version" )) ) + { + mcpu_version_action version_action; + + mcpu_version_parse_option( argv[1], &version_action ); + return( mcpu_version_print(prog, version_action) == 0 ? + EXIT_SUCCESS : EXIT_FAILURE ); + } + if( argc < 3 || parse_operation( argv[1], &options ) ) + { + usage( stderr, prog ); + return( EXIT_FAILURE ); + } + + archive_path = argv[2]; + member_argc = argc - 3; + member_argv = argv + 3; + exists = archive_exists( archive_path ); + mcpu_archive_init( &archive ); + + if( exists ) + { + err[0] = 0; + if( mcpu_archive_read( archive_path, &archive, err, sizeof( err ) ) ) + { + fprintf( stderr, "%s: %s: %s\n", prog, archive_path, err ); + goto out; + } + } + else if( options.operation != 'r' && options.operation != 'q' ) + { + fprintf( stderr, "%s: %s: %s\n", prog, archive_path, strerror( ENOENT ) ); + goto out; + } + + err[0] = 0; + switch( options.operation ) + { + case 'r': + case 'q': + if( operation_add( &archive, options.operation, member_argc, member_argv, + options.verbose, prog, err, sizeof( err ) ) ) + goto out; + mutation = 1; + break; + case 'd': + if( operation_delete( &archive, member_argc, member_argv, + options.verbose, prog ) ) + goto out; + mutation = 1; + break; + case 't': + if( operation_list( &archive, member_argc, member_argv, + options.verbose, prog ) ) + goto out; + rc = EXIT_SUCCESS; + goto out; + case 'p': + if( operation_print( &archive, member_argc, member_argv, + options.verbose, prog ) ) + goto out; + rc = EXIT_SUCCESS; + goto out; + case 'x': + if( operation_extract( &archive, member_argc, member_argv, + options.verbose, prog ) ) + goto out; + rc = EXIT_SUCCESS; + goto out; + case 's': + if( member_argc ) + { + fprintf( stderr, "%s: operation s does not accept member operands\n", + prog ); + goto out; + } + mutation = 1; + options.index = 1; + break; + default: + goto out; + } + + if( mutation ) + { + int write_index = options.index || archive.had_symbol_index; + + err[0] = 0; + if( mcpu_archive_write( archive_path, &archive, write_index, + err, sizeof( err ) ) ) + { + fprintf( stderr, "%s: %s: %s\n", prog, archive_path, err ); + goto out; + } + } + + rc = EXIT_SUCCESS; + +out: + mcpu_archive_free( &archive ); + return( rc ); + +} /* End of main() */ |
