#ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #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() */