diff options
Diffstat (limited to 'BeOS/ar-1.1/mwlib.c')
-rw-r--r-- | BeOS/ar-1.1/mwlib.c | 711 |
1 files changed, 711 insertions, 0 deletions
diff --git a/BeOS/ar-1.1/mwlib.c b/BeOS/ar-1.1/mwlib.c new file mode 100644 index 0000000..f3b8660 --- /dev/null +++ b/BeOS/ar-1.1/mwlib.c @@ -0,0 +1,711 @@ +/* +** mwlib.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 <errno.h> +#include <kernel/fs_attr.h> +#include <fcntl.h> /* is open() really here?!? sheesh... */ +#include <storage/Mime.h> +#include <sys/stat.h> + +#include "mwlib.h" +#include "copy_attrs.h" + +static const char *rcs_version_id = "$Id$"; + +/* ---------------------------------------------------------------------- +** Local prototypes +*/ +static status_t load_MWLibFile( FILE *file, MWLibFile *libfile ); +static size_t fwrite_big32( uint32 x, FILE *fp ); +static size_t fwrite_big32_seek( uint32 x, long offset, FILE *fp ); +static status_t add_object_sizes( MWObject *obj, uint32 *code, uint32 *data ); + +/* ---------------------------------------------------------------------- +** Load a Metrowerks library file into the given MWLib object. +** +** Returns: +** B_OK - all is well +** B_FILE_NOT_FOUND - can't open the given file +** B_IO_ERROR - can't read from the given file +** B_BAD_VALUE - invalid magic word in the file +** B_MISMATCHED_VALUES - invalid processor value (ie, not a PowerPC lib), +** or the magicflags member is not 0, or the file +** version number isn't 1. +** B_NO_MEMORY - unable to allocate memory while loading the lib +*/ +status_t load_MW_lib( MWLib *lib, const char *filename ) +{ + FILE *fp = NULL; + size_t recs = 0; + status_t retval = B_OK; + uint32 tmp32 = 0; + uint32 idx = 0; + char obj_name[PATH_MAX]; + + ASSERT( lib != NULL ); + ASSERT( filename != NULL ); + + fp = fopen( filename, "r" ); + if( !fp ) { + return B_FILE_NOT_FOUND; + } + + /* Read and check the magic number. + */ + recs = fread( &tmp32, sizeof( uint32 ), 1, fp ); + lib->header.magicword = B_BENDIAN_TO_HOST_INT32( tmp32 ); + if( recs != 1 ) { + retval = B_IO_ERROR; + goto close_return; + } else if( lib->header.magicword != MWLIB_MAGIC_WORD ) { + retval = B_BAD_VALUE; + goto close_return; + } + + /* Read and check the processor. + */ + recs = fread( &tmp32, sizeof( uint32 ), 1, fp ); + lib->header.magicproc = B_BENDIAN_TO_HOST_INT32( tmp32 ); + if( recs != 1 ) { + retval = B_IO_ERROR; + goto close_return; + } else if( lib->header.magicproc != MWLIB_MAGIC_PROC ) { + retval = B_MISMATCHED_VALUES; + goto close_return; + } + + /* Read and check the flags. + */ + recs = fread( &tmp32, sizeof( uint32 ), 1, fp ); + lib->header.magicflags = B_BENDIAN_TO_HOST_INT32( tmp32 ); + if( recs != 1 ) { + retval = B_IO_ERROR; + goto close_return; + } else if( lib->header.magicflags != 0 ) { + retval = B_MISMATCHED_VALUES; + goto close_return; + } + + /* Read and check the file version. + */ + recs = fread( &tmp32, sizeof( uint32 ), 1, fp ); + lib->header.version = B_BENDIAN_TO_HOST_INT32( tmp32 ); + if( recs != 1 ) { + retval = B_IO_ERROR; + goto close_return; + } else if( lib->header.version != 1 ) { + retval = B_MISMATCHED_VALUES; + goto close_return; + } + + /* Read the code size, data size, and number of objects. + */ + recs = fread( &tmp32, sizeof( uint32 ), 1, fp ); + lib->header.code_size = B_BENDIAN_TO_HOST_INT32( tmp32 ); + if( recs != 1 ) { + retval = B_IO_ERROR; + goto close_return; + } + + recs = fread( &tmp32, sizeof( uint32 ), 1, fp ); + lib->header.data_size = B_BENDIAN_TO_HOST_INT32( tmp32 ); + if( recs != 1 ) { + retval = B_IO_ERROR; + goto close_return; + } + + recs = fread( &tmp32, sizeof( uint32 ), 1, fp ); + lib->header.num_objects = B_BENDIAN_TO_HOST_INT32( tmp32 ); + if( recs != 1 ) { + retval = B_IO_ERROR; + goto close_return; + } + + /* Load the MWLibFile objects from the lib. + */ + lib->files = (MWLibFile *)malloc( lib->header.num_objects * sizeof( MWLibFile ) ); + if( lib->files == NULL ) { + retval = B_NO_MEMORY; + goto close_return; + } + + for( idx = 0; idx < lib->header.num_objects; idx++ ) { + retval = load_MWLibFile( fp, &(lib->files[idx]) ); + if( retval != B_OK ) { + goto close_return; + } + } + + /* Load the file names and object data. + ** + ** The file name actually appears twice in the library file; according + ** to the docs, one is the file name, the other is the "full path name". + ** In all of the libraries on my system, they're the same. + */ + lib->names = (char **)malloc( lib->header.num_objects * sizeof( char * ) ); + lib->data = (char **)malloc( lib->header.num_objects * sizeof( char * ) ); + if( lib->names == NULL || lib->data == NULL ) { + retval = B_NO_MEMORY; + goto close_return; + } + + for( idx = 0; idx < lib->header.num_objects; idx ++ ) { + /* Load the name and copy it into the right spot. + */ + retval = fseek( fp, lib->files[idx].off_filename, SEEK_SET ); + if( retval ) { + retval = B_IO_ERROR; + goto close_return; + } + + if( fgets( obj_name, PATH_MAX, fp ) == NULL ) { + retval = B_IO_ERROR; + goto close_return; + } + + lib->names[idx] = strdup( obj_name ); + if( lib->names[idx] == NULL ) { + retval = B_NO_MEMORY; + goto close_return; + } + + /* Load the object data. + */ + lib->data[idx] = (char *)malloc( lib->files[idx].object_size ); + if( lib->data[idx] == NULL ) { + retval = B_NO_MEMORY; + goto close_return; + } + + retval = fseek( fp, lib->files[idx].off_object, SEEK_SET ); + if( retval ) { + retval = B_IO_ERROR; + goto close_return; + } + + recs = fread( lib->data[idx], lib->files[idx].object_size, 1, fp ); + if( recs != 1 ) { + retval = B_IO_ERROR; + goto close_return; + } + } + +close_return: + fclose( fp ); + return retval; +} + +/* ---------------------------------------------------------------------- +** Load the file header from a Metrowerks library file. +** +** Returns: +** B_OK - All is well +** B_IO_ERROR - Error reading the file +*/ +static status_t load_MWLibFile( FILE *file, MWLibFile *libfile ) +{ + size_t recs = 0; + uint32 tmp32 = 0; + + ASSERT( file != NULL ); + ASSERT( libfile != NULL ); + + /* Load the modification time. + */ + recs = fread( &tmp32, sizeof( uint32 ), 1, file ); + libfile->m_time = (time_t)B_BENDIAN_TO_HOST_INT32( tmp32 ); + if( recs != 1 ) { + return B_IO_ERROR; + } + + /* Load the various offsets. + */ + recs = fread( &tmp32, sizeof( uint32 ), 1, file ); + libfile->off_filename = B_BENDIAN_TO_HOST_INT32( tmp32 ); + if( recs != 1 ) { + return B_IO_ERROR; + } + + recs = fread( &tmp32, sizeof( uint32 ), 1, file ); + libfile->off_fullpath = B_BENDIAN_TO_HOST_INT32( tmp32 ); + if( recs != 1 ) { + return B_IO_ERROR; + } + + recs = fread( &tmp32, sizeof( uint32 ), 1, file ); + libfile->off_object = B_BENDIAN_TO_HOST_INT32( tmp32 ); + if( recs != 1 ) { + return B_IO_ERROR; + } + + /* Load the object size. + */ + recs = fread( &tmp32, sizeof( uint32 ), 1, file ); + libfile->object_size = B_BENDIAN_TO_HOST_INT32( tmp32 ); + if( recs != 1 ) { + return B_IO_ERROR; + } + + return B_OK; +} + +/* ---------------------------------------------------------------------- +** Write a Metrowerks library file. +** +** Returns: +** B_OK - all is well; doesn't necessarily mean the file was written +** properly, just that you didn't lose any data +** B_NO_MEMORY - unable to allocate offset buffers +** B_ERROR - problem with backup file (can't rename existing file) +** +** Note: +** If you use this in a long-lived program, it leaks memory; the MWLib +** contents are never free()'d. +** +** Two-pass technique: +** +** pass 1 - write file header (need CODE SIZE and DATA SIZE) +** write object headers (need offsets for FILENAME, FULL PATH, DATA) +** write filenames (store offsets for object headers) +** write padding (file size % 4 bytes) +** write file data (store offset for object header; add to code/data +** size for file header; pad data size % 4 bytes) +** +** pass 2 - fill in file header CODE SIZE and DATA SIZE +** fill in object headers FILENAME, FULL PATH, DATA +** +** You could avoid this by building up the file headers in memory, but this is +** easier (?) and I'm not that concerned with speed... +** +*/ + +typedef struct file_header_offsets { + long codesize_offset; + long datasize_offset; +} file_header_offsets; + +typedef struct obj_header_offsets { + long filename_offset; + uint32 filename_offset_value; + + long fullpath_offset; + uint32 fullpath_offset_value; + + long data_offset; + uint32 data_offset_value; +} obj_header_offsets; + +static size_t fwrite_big32( uint32 x, FILE *fp ) +{ + uint32 tmp32 = B_HOST_TO_BENDIAN_INT32( x ); + + ASSERT( fp != NULL ); + + return fwrite( &tmp32, sizeof(tmp32), 1, fp ); +} + +static size_t fwrite_big32_seek( uint32 x, long offset, FILE *fp ) +{ + uint32 tmp32 = B_HOST_TO_BENDIAN_INT32( x ); + + ASSERT( fp != NULL ); + + if( fseek( fp, offset, SEEK_SET ) ) { + return 0; + } + + return fwrite( &tmp32, sizeof(tmp32), 1, fp ); +} + +static status_t add_object_sizes( MWObject *obj, uint32 *code, uint32 *data ) +{ + ASSERT( obj != NULL ); + ASSERT( code != NULL ); + ASSERT( data != NULL ); + + if( B_BENDIAN_TO_HOST_INT32( obj->magic_word ) != 'MWOB' || + B_BENDIAN_TO_HOST_INT32( obj->arch ) != 'PPC ' ) { + return B_ERROR; + } + + *code += B_BENDIAN_TO_HOST_INT32( obj->code_size ); + *data += B_BENDIAN_TO_HOST_INT32( obj->data_size ); + + return B_OK; +} + +void setfiletype( const char *file, const char *type ) +{ + int fd; + attr_info fa; + ssize_t wrote_bytes; + + fd = open( file, O_RDWR ); + if( fd < 0 ) { + fprintf( stderr, "ar: can't open %s to write file type, %s", + file, strerror( errno ) ); + return; + } + + fa.type = B_MIME_STRING_TYPE; + fa.size = (off_t)(strlen( type ) + 1); + + wrote_bytes = fs_write_attr( fd, "BEOS:TYPE", fa.type, 0, + type, fa.size ); + if( wrote_bytes != (ssize_t)fa.size ) { + fprintf( stderr, "ar: couldn't write complete file type, %s", + strerror( errno ) ); + } + + close( fd ); +} + +status_t write_MW_lib( MWLib *lib, const char *filename ) +{ + char *padding = "\0\0\0\0"; + long pad_amount; + + file_header_offsets head_offs; + obj_header_offsets *obj_offs; + uint32 codesize = 0; + uint32 datasize = 0; + uint32 num_objects = 0; + + FILE *fp; + char *tmp_filename = NULL; + uint32 idx = 0; + size_t recs; + status_t retval; + + mode_t mode_bits = 0666; + + ASSERT( lib != NULL ); + ASSERT( filename != NULL ); + +#if 0 +/* Apparently, I don't understand umask()... */ + + /* Find the default file creation mask. The semantics of umask() suck. + */ + mode_bits = umask( (mode_t)0 ); + (void)umask( mode_bits ); +#endif + + /* Easier access to the number of objects. + */ + num_objects = lib->header.num_objects; + + /* Allocate some storage for keeping track of the things we need to + ** write out in the second pass. + */ + head_offs.codesize_offset = 0; + head_offs.datasize_offset = 0; + + obj_offs = (obj_header_offsets *)malloc( sizeof(obj_header_offsets) * + num_objects ); + if( obj_offs == NULL ) { + fprintf( stderr, "ar: can't allocate memory for writing file\n" ); + + return B_NO_MEMORY; + } + memset( obj_offs, 0, sizeof(obj_header_offsets) * num_objects ); + + /* If the file exists, move it somewhere so we can recover if there's + ** an error. + */ + retval = access( filename, F_OK ); + if( retval == 0 ) { + struct stat s; + + /* Preserve the mode bits. + */ + retval = stat( filename, &s ); + if( retval != 0 ) { + fprintf( stderr, "ar: can't stat %s, %s\n", filename, + strerror( errno ) ); + } else { + mode_bits = s.st_mode; + } + + tmp_filename = (char *)malloc( strlen( filename ) + 2 ); + if( tmp_filename == NULL ) { + fprintf( stderr, + "ar: can't allocate memory for temporary filename\n" ); + } else { + sprintf( tmp_filename, "%s~", filename ); + + retval = access( tmp_filename, F_OK ); + if( retval == 0 ) { + retval = unlink( tmp_filename ); + if( retval != 0 ) { + fprintf( stderr, "ar: can't unlink %s, %s\n", tmp_filename, + strerror( errno ) ); + + free( tmp_filename ); + tmp_filename = NULL; + } + } + + if( tmp_filename ) { + retval = rename( filename, tmp_filename ); + if( retval != 0 ) { + fprintf( stderr, "ar: can't move %s to backup, %s\n", + filename, strerror( errno ) ); + + return B_ERROR; + } + } + } + } + + /* Attempt to open the archive file. + */ + fp = fopen( filename, "w" ); + if( fp == NULL ) { + fprintf( stderr, "ar: can't open %s for write, %s\n", filename, + strerror( errno ) ); + goto error_return; + } + + /* ---------------------------------------------------------------------- + ** Write the file. Pass 1. + */ + recs = fwrite_big32( lib->header.magicword, fp ); + recs += fwrite_big32( lib->header.magicproc, fp ); + recs += fwrite_big32( lib->header.magicflags, fp ); + recs += fwrite_big32( lib->header.version, fp ); + if( recs != 4 ) { + fprintf( stderr, "ar: error writing header for %s, %s\n", filename, + strerror( errno ) ); + goto error_return; + } + + /* Keep track of the code/data size offsets. + */ + head_offs.codesize_offset = ftell( fp ); + recs = fwrite_big32( codesize, fp ); + head_offs.datasize_offset = ftell( fp ); + recs += fwrite_big32( datasize, fp ); + if( recs != 2 ) { + fprintf( stderr, "ar: error writing header for %s, %s\n", filename, + strerror( errno ) ); + goto error_return; + } + + recs = fwrite_big32( num_objects, fp ); + if( recs != 1 ) { + fprintf( stderr, "ar: error writing header for %s, %s\n", filename, + strerror( errno ) ); + goto error_return; + } + + /* Write the object headers. + */ + for( idx = 0; idx < num_objects; idx++ ) { + recs = fwrite_big32( lib->files[idx].m_time, fp ); + + /* Keep track of the offsets, and write 0 for now. + */ + obj_offs[idx].filename_offset = ftell( fp ); + recs += fwrite_big32( 0, fp ); + obj_offs[idx].fullpath_offset = ftell( fp ); + recs += fwrite_big32( 0, fp ); + obj_offs[idx].data_offset = ftell( fp ); + recs += fwrite_big32( 0, fp ); + + recs += fwrite_big32( lib->files[idx].object_size, fp ); + + if( recs != 5 ) { + fprintf( stderr, "ar: error writing object header for %s, %s\n", + filename, strerror( errno ) ); + goto error_return; + } + } + + /* Write the file names. + */ + for( idx = 0; idx < num_objects; idx++ ) { + /* Need to make sure that all the file names we write into the + ** library DO NOT HAVE PATHS IN THEM, the world might end or something. + */ + size_t name_len = 0; + char *name_ptr = strrchr( lib->names[idx], '/' ); + + if( name_ptr == NULL ) { + name_ptr = lib->names[idx]; + } else { + name_ptr++; + } + + name_len = strlen( name_ptr ) + 1; + + obj_offs[idx].filename_offset_value = ftell( fp ); + recs = fwrite( name_ptr, name_len, 1, fp ); + obj_offs[idx].fullpath_offset_value = ftell( fp ); + recs += fwrite( name_ptr, name_len, 1, fp ); + + if( recs != 2 ) { + fprintf( stderr, "ar: error writing object name for %s, %s\n", + filename, strerror( errno ) ); + goto error_return; + } + } + + /* Pad the file if necessary. + */ + pad_amount = ftell( fp ) % 4; + if( pad_amount > 0 ) { + recs = fwrite( padding, pad_amount, 1, fp ); + + if( recs != 1 ) { + fprintf( stderr, "ar: error padding file %s, %s\n", filename, + strerror( errno ) ); + goto error_return; + } + } + + /* Write the object file data. + */ + for( idx = 0; idx < num_objects; idx++ ) { + obj_offs[idx].data_offset_value = ftell( fp ); + + recs = fwrite( lib->data[idx], lib->files[idx].object_size, 1, fp ); + if( recs != 1 ) { + fprintf( stderr, "ar: writing object data for %s, %s\n", filename, + strerror( errno ) ); + goto error_return; + } + + /* Add up the code/data size. + */ + retval = add_object_sizes( (MWObject *)lib->data[idx], + &codesize, &datasize ); + if( retval != B_OK ) { + fprintf( stderr, "ar - warning: %s is not an object file!\n", + lib->names[idx] ); + goto error_return; + } + + pad_amount = ftell( fp ) % 4; + if( pad_amount > 0 ) { + recs = fwrite( padding, pad_amount, 1, fp ); + + if( recs != 1 ) { + fprintf( stderr, "ar: error padding file %s, %s\n", filename, + strerror( errno ) ); + goto error_return; + } + } + } + + /* ---------------------------------------------------------------------- + ** Write the offsets into the file. Pass 2. + */ + + /* Write the code/data sizes. + */ + recs = fwrite_big32_seek( codesize, head_offs.codesize_offset, fp ); + recs += fwrite_big32_seek( datasize, head_offs.datasize_offset, fp ); + if( recs != 2 ) { + fprintf( stderr, "ar - error writing code and data sizes, %s\n", + strerror( errno ) ); + goto error_return; + } + + /* Write the offsets for each file. + */ + for( idx = 0; idx < num_objects; idx++ ) { + recs = fwrite_big32_seek( obj_offs[idx].filename_offset_value, + obj_offs[idx].filename_offset, fp ); + recs += fwrite_big32_seek( obj_offs[idx].fullpath_offset_value, + obj_offs[idx].fullpath_offset, fp ); + recs += fwrite_big32_seek( obj_offs[idx].data_offset_value, + obj_offs[idx].data_offset, fp ); + + if( recs != 3 ) { + fprintf( stderr, "ar - error writing object offsets, %s\n", + strerror( errno ) ); + goto error_return; + } + } + + /* If all is OK, close the file and get out of here after nuking the + ** temp file (if any), preserving the original file mode, and preserving + ** the file attributes. + */ + + fclose( fp ); + + /* Preserve the original file mode bits. + */ + retval = chmod( filename, mode_bits ); + if( retval != 0 ) { + fprintf( stderr, "ar: unable to change file mode for %s, %s\n", + filename, strerror( errno ) ); + } + + /* Nuke the temp file (if any), after copying over any file attributes. + */ + if( tmp_filename != NULL ) { + retval = copy_attrs( filename, tmp_filename ); + + retval = unlink( tmp_filename ); + if( retval != 0 ) { + fprintf( stderr, "ar - error unlinking %s, %s\n", tmp_filename, + strerror( errno ) ); + } + free( tmp_filename ); + } else { + /* If there isn't a temp file, we should still give this new + ** file a file type attribute. + */ + setfiletype( filename, "application/x-mw-library" ); + } + + return B_OK; + +error_return: + /* Restore the original file if we had any problems. + */ + fclose( fp ); + + if( tmp_filename != NULL ) { + retval = unlink( filename ); + retval = rename( tmp_filename, filename ); + if( retval != 0 ) { + fprintf( stderr, "ar: can't restore %s to %s, %s\n", + tmp_filename, filename, strerror( errno ) ); + } + } + + return B_ERROR; +} |