diff options
Diffstat (limited to 'BeOS/ar-1.1/commands.c')
-rw-r--r-- | BeOS/ar-1.1/commands.c | 809 |
1 files changed, 809 insertions, 0 deletions
diff --git a/BeOS/ar-1.1/commands.c b/BeOS/ar-1.1/commands.c new file mode 100644 index 0000000..f9304d0 --- /dev/null +++ b/BeOS/ar-1.1/commands.c @@ -0,0 +1,809 @@ +/* +** commands.c - POSIX 1003.2 "ar" command +** +** This isn't a pure POSIX 1003.2 ar; it only manipulates Metrowerks +** Library files, not general-purpose POSIX 1003.2 format archives. +** +** Dec. 14, 1997 Chris Herborth (chrish@kagi.com) +** +** This code is donated to the PUBLIC DOMAIN. You can use, abuse, modify, +** redistribute, steal, or otherwise manipulate this code. No restrictions +** at all. If you laugh at this code, you can't use it. +** +** This "ar" was implemented using IEEE Std 1003.2-1992 as the basis for +** the interface, and Metrowerk's published docs detailing their library +** format. Look inside for clues about how reality differs from MW's +** documentation on BeOS... +*/ + +#include <support/Errors.h> +#include <support/byteorder.h> +#ifndef NO_DEBUG +#include <assert.h> +#define ASSERT(cond) assert(cond) +#else +#define ASSERT(cond) ((void)0) +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <limits.h> +#include <string.h> +#include <utime.h> +#include <errno.h> +#include <sys/stat.h> + +#include "mwlib.h" +#include "commands.h" + +#ifndef FALSE +#define FALSE (0) +#endif +#ifndef TRUE +#define TRUE (!FALSE) +#endif + +static const char *rcs_version_id = "$Id$"; + +/* ---------------------------------------------------------------------- +** Local functions +** +** do_match() - find the index of the file, if it's in the archive; return +** TRUE if found, else FALSE +**/ +static int do_match( MWLib *lib, const char *file, int *idx ); + +static int do_match( MWLib *lib, const char *file, int *idx ) +{ + int which = 0; + char *name_ptr; + + ASSERT( lib != NULL ); + ASSERT( file != NULL ); + ASSERT( idx != NULL ); + + /* Skip over the path, if any, so we can compare just the file name. + */ + name_ptr = strrchr( file, '/' ); + if( name_ptr == NULL ) { + name_ptr = (char *)file; + } else { + name_ptr++; + } + + for( which = 0; which < lib->header.num_objects; which++ ) { + if( !strcmp( name_ptr, lib->names[which] ) ) { + *idx = which; + return TRUE; + } + } + + return FALSE; +} + +/* ---------------------------------------------------------------------- +** Delete an archive member. +** +** This isn't really optimal; you could make a more efficient version +** using a linked list instead of arrays for the data. This was easier +** to write, and speed shouldn't be that big a deal here... you're not +** likely to be dealing with thousands of files. +*/ +static status_t delete_lib_entry( MWLib *lib, int idx, int verbose ); + +status_t do_delete( const char *archive_name, char **files, int verbose ) +{ + status_t retval = B_OK; + MWLib lib; + int idx = 0; + int which = 0; + + ASSERT( archive_name != NULL ); + + if( files == NULL ) { + fprintf( stderr, "ar: %s, nothing to do\n", archive_name ); + return B_ERROR; + } + + retval = load_MW_lib( &lib, archive_name ); + if( retval != B_OK ) { + switch( retval ) { + case B_FILE_NOT_FOUND: + fprintf( stderr, "ar: %s, file not found\n", archive_name ); + return retval; + break; + + default: + return retval; + break; + } + } + + /* Delete the specified files. + */ + for( idx = 0; files[idx] != NULL; idx++ ) { + if( do_match( &lib, files[idx], &which ) ) { + retval = delete_lib_entry( &lib, which, verbose ); + } + which = 0; + } + + /* Write the new file. + */ + retval = write_MW_lib( &lib, archive_name ); + + return retval; +} + +static status_t delete_lib_entry( MWLib *lib, int which, int verbose ) +{ + uint32 new_num; + MWLibFile *new_files = NULL; + char **new_names = NULL; + char **new_data = NULL; + int ctr = 0; + int idx = 0; + + ASSERT( lib != NULL ); + ASSERT( which <= lib->header.num_objects ); + + new_num = lib->header.num_objects - 1; + + new_files = (MWLibFile *)malloc( new_num * ( sizeof( MWLibFile ) ) ); + new_names = (char **)malloc( new_num * ( sizeof( char * ) ) ); + new_data = (char **)malloc( new_num * ( sizeof( char * ) ) ); + if( new_files == NULL || new_names == NULL || new_data == NULL ) { + return B_NO_MEMORY; + } + + /* Copy the contents of the old lib to the new lib, skipping the one + ** we want to delete. + */ + for( ctr = 0; ctr < lib->header.num_objects; ctr++ ) { + if( ctr != which ) { + memcpy( &(new_files[idx]), &(lib->files[ctr]), + sizeof( MWLibFile ) ); + new_names[idx] = lib->names[ctr]; + new_data[idx] = lib->data[ctr]; + + idx++; + } else { + /* Free up the name and data. + */ + if( verbose ) { + printf( "d - %s\n", lib->names[ctr] ); + } + + free( lib->names[idx] ); + lib->names[idx] = NULL; + free( lib->data[idx] ); + lib->data[idx] = NULL; + } + } + + /* Free up the old lib's data. + */ + free( lib->files ); + free( lib->names ); + free( lib->data ); + + lib->files = new_files; + lib->names = new_names; + lib->data = new_data; + + lib->header.num_objects = new_num; + + return B_OK; +} + +/* ---------------------------------------------------------------------- +** Print an archive member to stdout. +*/ +static status_t print_lib_entry( MWLib *lib, int idx, int verbose ); + +status_t do_print( const char *archive_name, char **files, int verbose ) +{ + status_t retval = B_OK; + MWLib lib; + int idx = 0; + + ASSERT( archive_name != NULL ); + + retval = load_MW_lib( &lib, archive_name ); + if( retval != B_OK ) { + switch( retval ) { + case B_FILE_NOT_FOUND: + fprintf( stderr, "ar: %s, file not found\n", archive_name ); + return retval; + break; + + default: + return retval; + break; + } + } + + if( files == NULL ) { + /* Then we print the entire archive. + */ + for( idx = 0; idx < lib.header.num_objects; idx++ ) { + retval = print_lib_entry( &lib, idx, verbose ); + } + } else { + /* Then we print the specified files. + */ + int which = 0; + + for( idx = 0; files[idx] != NULL; idx++ ) { + if( do_match( &lib, files[idx], &which ) ) { + retval = print_lib_entry( &lib, which, verbose ); + } + which = 0; + } + } + + return retval; +} + +static status_t print_lib_entry( MWLib *lib, int idx, int verbose ) +{ + int recs; + + ASSERT( lib != NULL ); + + if( verbose ) { + printf( "\n<%s>\n\n", lib->names[idx] ); + } + + recs = fwrite( lib->data[idx], lib->files[idx].object_size, 1, stdout ); + fflush( stdout ); + if( recs != 1 ) { + fprintf( stderr, "error printing %s, %s\n", lib->names[idx], + strerror( errno ) ); + return B_OK; + } + + return B_OK; +} + +/* ---------------------------------------------------------------------- +** Add/replace/update files in an archive. +*/ +static status_t add_lib_entry( MWLib *lib, const char *filename, int verbose ); +static status_t replace_lib_entry( MWLib *lib, const char *filename, + int idx, int verbose ); +static status_t load_lib_file( const char *filename, + char **data, MWLibFile *info ); + +static status_t load_lib_file( const char *filename, + char **data, MWLibFile *info ) +{ + status_t retval = B_OK; + struct stat s; + FILE *fp; + uint32 recs; + + ASSERT( filename != NULL ); + ASSERT( info != NULL ); + + /* Initialize the info area. + */ + info->m_time = (time_t)0; /* Only this... */ + info->off_filename = 0; + info->off_fullpath = 0; + info->off_object = 0; + info->object_size = 0; /* ... and this will actually be updated. */ + + /* stat() the file to get the info we need. + */ + retval = stat( filename, &s ); + if( retval != 0 ) { + fprintf( stderr, "ar: can't stat %s, %s\n", filename, + strerror( errno ) ); + return B_FILE_NOT_FOUND; + } + + /* Possible errors here; if you have an object that's larger + ** than a size_t can hold (malloc() can only allocate a size_t size, + ** not a full off_t)... + */ + if( s.st_size > (off_t)ULONG_MAX ) { + fprintf( stderr, "ar: %s is too large!\n", filename ); + return B_NO_MEMORY; + } + + /* Allocate enough memory to hold the file data. + */ + *data = (char *)malloc( (size_t)s.st_size ); + if( *data == NULL ) { + fprintf( stderr, "ar: can't allocate memory for %s\n", filename ); + return B_NO_MEMORY; + } + + /* Read the file's data. + */ + fp = fopen( filename, "r" ); + if( fp == NULL ) { + fprintf( stderr, "ar: can't open %s, %s\n", filename, + strerror( errno ) ); + retval = B_FILE_NOT_FOUND; + goto free_data_return; + } + + recs = fread( *data, (size_t)s.st_size, 1, fp ); + if( recs != 1 ) { + fprintf( stderr, "ar: can't read %s, %s\n", filename, + strerror( errno ) ); + retval = B_IO_ERROR; + goto close_fp_return; + } + + fclose( fp ); + + /* Now that all the stuff that can fail has succeeded, fill in the info + ** we need. + */ + info->m_time = s.st_mtime; + info->object_size = (uint32)s.st_size; + + return B_OK; + + /* How we should return if an error occurred. + */ +close_fp_return: + fclose( fp ); + +free_data_return: + free( *data ); + *data = NULL; + + return retval; +} + +status_t do_replace( const char *archive_name, char **files, int verbose, + int create, int update ) +{ + status_t retval = B_OK; + MWLib lib; + int idx = 0; + int which = 0; + + ASSERT( archive_name != NULL ); + + memset( &lib, 0, sizeof( MWLib ) ); + + if( files == NULL ) { + fprintf( stderr, "ar: %s, nothing to do\n", archive_name ); + return B_ERROR; + } + + retval = load_MW_lib( &lib, archive_name ); + if( retval != B_OK ) { + switch( retval ) { + case B_FILE_NOT_FOUND: + lib.header.magicword = 'MWOB'; + lib.header.magicproc = 'PPC '; + lib.header.magicflags = 0; + lib.header.version = 1; + + if( lib.files != NULL ) { + free( lib.files ); + lib.files = NULL; + } + + if( lib.names != NULL ) { + free( lib.names ); + lib.names = NULL; + } + + if( lib.data != NULL ) { + lib.data = NULL; + } + + if( !create ) { + fprintf( stderr, "ar: creating %s\n", archive_name ); + } + + if( update ) { + fprintf( stderr, "ar: nothing to do for %s\n", archive_name ); + return retval; + } + break; + + default: + return retval; + break; + } + } + + for( idx = 0; files[idx] != NULL; idx++ ) { + if( do_match( &lib, files[idx], &which ) ) { + /* Then the file exists, and we need to replace it or update it. + */ + if( update ) { + /* Compare m_times + ** then replace this entry + */ + struct stat s; + + retval = stat( files[idx], &s ); + if( retval != 0 ) { + fprintf( stderr, "ar: can't stat %s, %s\n", files[idx], + strerror( errno ) ); + } + + if( s.st_mtime >= lib.files[which].m_time ) { + retval = replace_lib_entry( &lib, files[idx], which, + verbose ); + } else { + fprintf( stderr, "ar: a newer %s is already in %s\n", + files[idx], archive_name ); + } + } else { + /* replace this entry + */ + retval = replace_lib_entry( &lib, files[idx], which, verbose ); + } + } else { + /* add this entry + */ + retval = add_lib_entry( &lib, files[idx], verbose ); + } + } + + /* Write the new file. + */ + retval = write_MW_lib( &lib, archive_name ); + + return retval; +} + +static status_t add_lib_entry( MWLib *lib, const char *filename, int verbose ) +{ + status_t retval = B_OK; + uint32 new_num_objects; + uint32 idx; + + ASSERT( lib != NULL ); + ASSERT( filename != NULL ); + + /* Find out how many objects we'll have after we add this one. + */ + new_num_objects = lib->header.num_objects + 1; + idx = lib->header.num_objects; + + /* Attempt to reallocate the MWLib's buffers. If one of these fails, + ** we could leak a little memory, but it shouldn't be a big deal in + ** a short-lived app like this. + */ + lib->files = (MWLibFile *)realloc( lib->files, + sizeof(MWLibFile) * new_num_objects ); + lib->names = (char **)realloc( lib->names, + sizeof(char *) * new_num_objects ); + lib->data = (char **)realloc( lib->data, + sizeof(char *) * new_num_objects ); + if( lib->files == NULL || lib->names == NULL || lib->data == NULL ) { + fprintf( stderr, "ar: can't allocate memory to add %s\n", filename ); + return B_NO_MEMORY; + } + + /* Load the file's data and info into the MWLib structure. + */ + retval = load_lib_file( filename, &(lib->data[idx]), &(lib->files[idx]) ); + if( retval != B_OK ) { + fprintf( stderr, "ar: error adding %s, %s\n", filename, + strerror( errno ) ); + + return retval; + } + + /* Save a copy of the filename. This is where we leak + ** sizeof(MWLibFile) + 2 * sizeof(char *) bytes because we don't + ** shrink lib->files, lib->names, and lib->data. + */ + lib->names[idx] = strdup( filename ); + if( lib->names == NULL ) { + fprintf( stderr, "ar: error allocating memory for filename\n" ); + + return B_NO_MEMORY; + } + + /* Now that everything's OK, we can update the MWLib header. + */ + lib->header.num_objects++; + + /* Give a little feedback. + */ + if( verbose ) { + printf( "a - %s\n", filename ); + } + + return B_OK; +} + +static status_t replace_lib_entry( MWLib *lib, const char *filename, + int idx, int verbose ) +{ + char *buff; + MWLibFile info; + char *dup_name; + + status_t retval = B_OK; + + ASSERT( lib != NULL ); + ASSERT( filename != NULL ); + ASSERT( idx <= lib->header.num_objects ); + + /* Load the file's data and info into the MWLib structure. + ** + ** We'll do it safely so we don't end up writing a bogus library in + ** the event of failure. + */ + retval = load_lib_file( filename, &buff, &info ); + if( retval != B_OK ) { + fprintf( stderr, "ar: error adding %s, %s\n", filename, + strerror( errno ) ); + + return retval; + } + + /* Attempt to allocate memory for a duplicate of the file name. + */ + dup_name = strdup( filename ); + if( dup_name == NULL ) { + fprintf( stderr, "ar: unable to allocate memory for filename\n", + filename ); + + free( buff ); + + return B_NO_MEMORY; + } + + /* All is well, so let's update the MWLib object appropriately. + */ + lib->files[idx].m_time = info.m_time; + lib->files[idx].off_filename = 0; + lib->files[idx].off_fullpath = 0; + lib->files[idx].off_object = 0; + lib->files[idx].object_size = info.object_size; + + lib->data[idx] = buff; + + free( lib->names[idx] ); + lib->names[idx] = dup_name; + + /* Give a little feedback. + */ + if( verbose ) { + printf( "r - %s\n", filename ); + } + + return B_OK; +} + +/* ---------------------------------------------------------------------- +** Print the table for an archive. +*/ +static status_t table_lib_entry( MWLib *lib, int idx, int verbose ); + +status_t do_table( const char *archive_name, char **files, int verbose ) +{ + status_t retval = B_OK; + MWLib lib; + int idx = 0; + + ASSERT( archive_name != NULL ); + + retval = load_MW_lib( &lib, archive_name ); + if( retval != B_OK ) { + switch( retval ) { + case B_FILE_NOT_FOUND: + fprintf( stderr, "ar: %s, file not found\n", archive_name ); + return retval; + break; + + default: + return retval; + break; + } + } + + if( files == NULL ) { + /* Then we print the table for the entire archive. + */ + for( idx = 0; idx < lib.header.num_objects; idx++ ) { + retval = table_lib_entry( &lib, idx, verbose ); + } + } else { + /* Then we print the table for the specified files. + */ + int which = 0; + + for( idx = 0; files[idx] != NULL; idx++ ) { + if( do_match( &lib, files[idx], &which ) ) { + retval = table_lib_entry( &lib, which, verbose ); + } + which = 0; + } + } + + return retval; +} + +static status_t table_lib_entry( MWLib *lib, int idx, int verbose ) +{ + struct tm *t; + char month_buff[4]; + + ASSERT( lib != NULL ); + + if( verbose ) { + t = localtime( &(lib->files[idx].m_time) ); + if( t == NULL ) { + fprintf( stderr, "localtime() failed, %s\n", + strerror( errno ) ); + return B_OK; + } + + if( strftime( month_buff, sizeof( month_buff ), + "%b", t ) == 0 ) { + /* TODO: error message */ + fprintf( stderr, "strftime() failed, %s\n", + strerror( errno ) ); + return B_OK; + } + + /* I wish POSIX allowed for a nicer format; even using tabs + * between some entries would be better. + */ + printf( "%s %u/%u %u %s %d %d:%d %d %s\n", + "-rw-r--r--", /* simulated mode */ + getuid(), getgid(), /* simulated uid & gid */ + lib->files[idx].object_size, + month_buff, /* abbreviated month */ + t->tm_mon, /* day of month */ + t->tm_hour, /* hour */ + t->tm_min, /* minute */ + t->tm_year, /* year */ + lib->names[idx] ); + } else { + printf( "%s\n", lib->names[idx] ); + } + + return B_OK; +} + +/* ---------------------------------------------------------------------- +** Extract one or more files from the archive. +*/ +static status_t extract_lib_entry( MWLib *lib, int idx, int verbose ); + +status_t do_extract( const char *archive_name, char **files, int verbose ) +{ + status_t retval = B_OK; + MWLib lib; + int idx = 0; + + ASSERT( archive_name != NULL ); + + retval = load_MW_lib( &lib, archive_name ); + if( retval != B_OK ) { + switch( retval ) { + case B_FILE_NOT_FOUND: + fprintf( stderr, "ar: %s, file not found\n", archive_name ); + return retval; + break; + + default: + return retval; + break; + } + } + + if( files == NULL ) { + /* Then we extract all the files. + */ + for( idx = 0; idx < lib.header.num_objects; idx++ ) { + retval = extract_lib_entry( &lib, idx, verbose ); + } + } else { + /* Then we extract the specified files. + */ + int which = 0; + + for( idx = 0; files[idx] != NULL; idx++ ) { + if( do_match( &lib, files[idx], &which ) ) { + retval = extract_lib_entry( &lib, which, verbose ); + } + which = 0; + } + } + + return retval; +} + +static status_t extract_lib_entry( MWLib *lib, int idx, int verbose ) +{ + FILE *fp; + int recs; + status_t retval = B_OK; + struct stat s; + mode_t mode_bits = 0666; /* TODO: use user's umask() instead */ + + ASSERT( lib != NULL ); + + /* Delete the file if it already exists. + */ + retval = access( lib->names[idx], F_OK ); + if( retval == 0 ) { + retval = stat( lib->names[idx], &s ); + if( retval != 0 ) { + fprintf( stderr, "ar: can't stat %s, %s\n", lib->names[idx], + strerror( errno ) ); + } else { + mode_bits = s.st_mode; + } + retval = unlink( lib->names[idx] ); + if( retval != 0 ) { + fprintf( stderr, "ar: can't unlink %s, %s\n", lib->names[idx], + strerror( retval ) ); + return B_OK; + } + } + + /* Write the file. + */ + if( verbose ) { + printf( "x - %s\n", lib->names[idx] ); + } + + fp = fopen( lib->names[idx], "w" ); + if( fp == NULL ) { + fprintf( stderr, "ar: can't open %s for write, %s\n", lib->names[idx], + strerror( errno ) ); + return B_OK; + } + + recs = fwrite( lib->data[idx], lib->files[idx].object_size, 1, fp ); + if( recs != 1 ) { + fprintf( stderr, "error writing %s, %s\n", lib->names[idx], + strerror( errno ) ); + } + + retval = fclose( fp ); + + /* Set the newly extracted file's modification time to the time + ** stored in the archive. + */ + retval = stat( lib->names[idx], &s ); + if( retval != 0 ) { + fprintf( stderr, "ar: can't stat %s, %s\n", lib->names[idx], + strerror( errno ) ); + } else { + struct utimbuf new_times; + + new_times.actime = s.st_atime; + new_times.modtime = lib->files[idx].m_time; + + retval = utime( lib->names[idx], &new_times ); + if( retval != 0 ) { + fprintf( stderr, "ar: can't set modification time for %s, %s\n", + lib->names[idx], strerror( retval ) ); + } + } + + /* Set the newly extracted file's mode. + */ + retval = chmod( lib->names[idx], mode_bits ); + if( retval != 0 ) { + fprintf( stderr, "ar: unable to change file mode for %s, %s\n", + lib->names[idx], strerror( errno ) ); + } + + /* Set the newly extracted file's type. + */ + setfiletype( lib->names[idx], "application/x-mw-library" ); + + return B_OK; +} |