summaryrefslogtreecommitdiff
path: root/utils/mcpu-ar.c
diff options
context:
space:
mode:
Diffstat (limited to 'utils/mcpu-ar.c')
-rw-r--r--utils/mcpu-ar.c542
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() */