diff options
Diffstat (limited to 'BeOS/ar-1.1')
-rw-r--r-- | BeOS/ar-1.1/Makefile | 48 | ||||
-rw-r--r-- | BeOS/ar-1.1/README.html | 1 | ||||
-rw-r--r-- | BeOS/ar-1.1/README.txt | 29 | ||||
-rw-r--r-- | BeOS/ar-1.1/ar | bin | 0 -> 16355 bytes | |||
-rw-r--r-- | BeOS/ar-1.1/ar.xMAP | 461 | ||||
-rw-r--r-- | BeOS/ar-1.1/commands.c | 809 | ||||
-rw-r--r-- | BeOS/ar-1.1/commands.h | 28 | ||||
-rw-r--r-- | BeOS/ar-1.1/copy_attrs.c | 128 | ||||
-rw-r--r-- | BeOS/ar-1.1/copy_attrs.h | 24 | ||||
-rw-r--r-- | BeOS/ar-1.1/docs/ar.html | 234 | ||||
-rw-r--r-- | BeOS/ar-1.1/docs/dumpar.py | 271 | ||||
-rw-r--r-- | BeOS/ar-1.1/docs/dumpar.pyc | bin | 0 -> 5827 bytes | |||
-rw-r--r-- | BeOS/ar-1.1/docs/dumpo.py | 126 | ||||
-rw-r--r-- | BeOS/ar-1.1/docs/notes | 34 | ||||
-rw-r--r-- | BeOS/ar-1.1/main.c | 312 | ||||
-rw-r--r-- | BeOS/ar-1.1/mwlib.c | 711 | ||||
-rw-r--r-- | BeOS/ar-1.1/mwlib.h | 118 |
17 files changed, 3334 insertions, 0 deletions
diff --git a/BeOS/ar-1.1/Makefile b/BeOS/ar-1.1/Makefile new file mode 100644 index 0000000..8fb11ec --- /dev/null +++ b/BeOS/ar-1.1/Makefile @@ -0,0 +1,48 @@ +###################################################################### +# Makefile for ar +# +# Dec. 14, 1997 Chris Herborth (chrish@kagi.com) +# +# $Id$ +###################################################################### + +AR_VERSION=1.1 + +# Make variables +CC=mwcc +LD=mwcc + +CFLAGS=-w9 -rostr -O3 -g +CFLAGS_O=-w9 -rostr -O7 -opt schedule604 +LDFLAGS=-g -map ar.xMAP +LDFLAGS_O= + +INSTALL=install -m 755 + +DESTINATION=/boot/home/config/bin + +PARTS=main.o mwlib.o commands.o copy_attrs.o + +all: ar + +nodebug: + -rm -f ar $(PARTS) ar.dbg ar.xSYM + $(MAKE) CFLAGS="$(CFLAGS_O) -DNO_DEBUG" LDFLAGS="$(LDFLAGS_O)" ar + +ar: $(PARTS) + $(LD) $(LDFLAGS) -o $@ $(PARTS) + +install: ar + $(INSTALL) ar $(DESTINATION) + ln -sf $(DESTINATION)/ar $(DESTINATION)/ar-posix + +clean: + -rm -f $(PARTS) ar ar.dbg ar.xSYM + +zip: + (cd .. ; zip -9ry ar-$(AR_VERSION).zip ar-$(AR_VERSION) \ + -x ar-$(AR_VERSION)/RCS -x ar-$(AR_VERSION)/docs/RCS \ + -x ar-$(AR_VERSION)/RCS/\* -x ar-$(AR_VERSION)/docs/RCS/\*) + +%.o: %.c + $(CC) $(CFLAGS) -c $< -o $@ diff --git a/BeOS/ar-1.1/README.html b/BeOS/ar-1.1/README.html new file mode 100644 index 0000000..607c03a --- /dev/null +++ b/BeOS/ar-1.1/README.html @@ -0,0 +1 @@ +docs/ar.html
\ No newline at end of file diff --git a/BeOS/ar-1.1/README.txt b/BeOS/ar-1.1/README.txt new file mode 100644 index 0000000..f995789 --- /dev/null +++ b/BeOS/ar-1.1/README.txt @@ -0,0 +1,29 @@ +ar - POSIX 1003.2 interface to library files + +Here's the source and PowerPC binary for a POSIX 1003.2 interface "ar" +command; this is extremely useful when you're porting complex UNIX/POSIX +software to BeOS for PowerPC (I originally wrote it to support my Python +port). + +To build/install ar, do this in a Terminal: + +make nodebug install + +This will create ar and ar-posix (a symlink to ar) in ~/config/bin. The +ar-posix symlink is to make things a little easier if you happen to +have GeekGadgets (see www.ninemoons.com) installed; it comes with an +ar that only works on objects/libraries produced by GNU C for BeOS. + +To use the POSIX ar with your port, do something like this: + +AR=ar-posix ./configure ... normal configure arguments ... + +and then: + +make AR=ar-posix + +You may need to check the Makefiles; people seem to be quite sloppy about +using just plain "ar cr libfoo.a ..." instead of "$(AR) cr libfoo.a ...". + +- Chris Herborth, April 18, 1998 + (chrish@kagi.com) diff --git a/BeOS/ar-1.1/ar b/BeOS/ar-1.1/ar Binary files differnew file mode 100644 index 0000000..e44e2dc --- /dev/null +++ b/BeOS/ar-1.1/ar diff --git a/BeOS/ar-1.1/ar.xMAP b/BeOS/ar-1.1/ar.xMAP new file mode 100644 index 0000000..bee438d --- /dev/null +++ b/BeOS/ar-1.1/ar.xMAP @@ -0,0 +1,461 @@ +File: 0 "/boot/src/ar-1.0/main.c" +File: 1 "/boot/src/ar-1.0/mwlib.c" +File: 2 "/boot/src/ar-1.0/commands.c" +File: 3 "/boot/src/ar-1.0/copy_attrs.c" +File: 5 "/boot/rel/src/kit/glue/common/global_destructor_chain.c" +File: 6 "/boot/rel/src/kit/glue/ppc/runtime.c" +File: 9 "/boot/rel/src/kit/glue/common/init_term_dyn.c" +File: 10 "/boot/rel/src/kit/glue/common/start_dyn.c" +File: 11 "/boot/develop/lib/ppc/libroot.so" + +Files that were not referenced: +"libbe.so" +"libtracker.so" +"libmedia.so" +"libnet.so" +"libnetdev.so" +"libdevice.so" +"libmidi.so" +"libgame.so" +"libatalk.so" +"libmail.so" + +Code section, size = 16672 bytes +000000 PR .__sinit file = "*Linker-Generated*" +00001C PR .usage file = "main.c" +000050 PR .version +000094 PR .check_command +000128 PR .main +0005A0 PR .load_MW_lib file = "mwlib.c" +000A1C PR .load_MWLibFile +000BAC PR .fwrite_big32 +000C10 PR .fwrite_big32_seek +000CA4 PR .add_object_sizes +000DA4 PR .setfiletype +000EA8 PR .write_MW_lib +001850 PR .do_match file = "commands.c" +001958 PR .do_delete +001A84 PR .delete_lib_entry +001C5C PR .do_print +001D98 PR .print_lib_entry +001E90 PR .load_lib_file +0020F4 PR .do_replace +0023B8 PR .add_lib_entry +00256C PR .replace_lib_entry +00272C PR .do_table +002868 PR .table_lib_entry +0029E8 PR .do_extract +002B24 PR .extract_lib_entry +002E20 PR .copy_attrs file = "copy_attrs.c" +00306C PR .__destroy_global_chain file = "global_destructor_chain.c" +0030C0 PR .__RTOC file = "init_term_dyn.c" +0030C8 PR ._init_routine_ +003118 PR ._term_routine_ +003148 PR .__start file = "start_dyn.c" +003204 GL .__register_fragment file = "*Linker-Generated*" +00321C GL .find_thread +003234 GL .memcpy +00324C GL ._call_init_routines_ +003264 GL .printf +00327C GL .exit +003294 GL .getopt +0032AC GL .malloc +0032C4 GL .__assertion_failed +0032DC GL .fprintf +0032F4 GL .fopen +00330C GL .fread +003324 GL .fseek +00333C GL .fgets +003354 GL .strdup +00336C GL .fclose +003384 GL .strrchr +00339C GL .strcmp +0033B4 GL .free +0033CC GL .memset +0033E4 GL .access +0033FC GL .stat +003414 GL ._errnop +00342C GL .strerror +003444 GL .strlen +00345C GL .sprintf +003474 GL .unlink +00348C GL .rename +0034A4 GL .fwrite +0034BC GL .ftell +0034D4 GL .chmod +0034EC GL .open +003504 GL .close +00351C GL .fs_fopen_attr_dir +003534 GL .fs_read_attr_dir +00354C GL .fs_stat_attr +003564 GL .fs_read_attr +00357C GL .fs_write_attr +003594 GL .fflush +0035AC GL .realloc +0035C4 GL .localtime +0035DC GL .strftime +0035F4 GL .getgid +00360C GL .getuid +003624 GL .utime +00363C GL ._thread_do_exit_notification +003654 GL .__unregister_fragment +00366C GL .__ptr_glue file = "runtime.c" +003680 RO @10 file = "main.c" +003690 RO @13 +0036B4 RO @16 +0036D8 RO @17 +0036FC RO @18 +003739 RO @30 +003769 RO @109 +00378A RO @110 +0037A2 RO @111 +0037BA RO @112 +0037D3 RO @113 +0037DD RO @114 +0037FD RO @115 +003815 RO @116 +003829 RO @117 +00383D RO @118 +003871 RO @119 +00388C RO @120 +0038A5 RO @121 +0038B9 RO @78 file = "mwlib.c" +0038C3 RO @79 +0038CB RO @80 +0038DA RO @81 +0038DC RO @98 +0038E7 RO @99 +0038F5 RO @104 +0038FE RO @122 +003908 RO @123 +003913 RO @124 +00391E RO @133 +003947 RO @134 +003951 RO @135 +00397B RO @235 +003980 RO @236 +0039AC RO @237 +0039C3 RO @238 +0039F5 RO @239 +0039F9 RO @240 +003A12 RO @241 +003A33 RO @242 +003A35 RO @243 +003A56 RO @244 +003A7B RO @245 +003AA7 RO @246 +003AD1 RO @247 +003AF0 RO @248 +003B14 RO @249 +003B3D RO @250 +003B69 RO @251 +003B90 RO @252 +003BBB RO @253 +003BD8 RO @254 +003BF1 RO @255 +003C11 RO @29 file = "commands.c" +003C1B RO @30 +003C26 RO @31 +003C31 RO @32 +003C3B RO @53 +003C4E RO @54 +003C65 RO @55 +003C7D RO @81 +003C9E RO @82 +003CA6 RO @120 +003CAE RO @121 +003CC5 RO @142 +003CD4 RO @143 +003CDF RO @144 +003CF6 RO @145 +003D0C RO @146 +003D2E RO @147 +003D30 RO @148 +003D47 RO @149 +003D5E RO @198 +003D6F RO @199 +003D89 RO @200 +003DAA RO @222 +003DCF RO @223 +003DE8 RO @224 +003E12 RO @225 +003E1A RO @242 +003E39 RO @243 +003E65 RO @244 +003E6D RO @284 +003E85 RO @285 +003E88 RO @286 +003E9F RO @287 +003EBE RO @288 +003EC9 RO @289 +003ECD RO @343 +003EE6 RO @344 +003EEE RO @345 +003EF0 RO @346 +003F11 RO @347 +003F27 RO @348 +003F53 RO @349 +003F7E RO @350 +003F97 RO @43 file = "copy_attrs.c" +003FA6 RO @44 +003FB3 RO @45 +003FC4 TI @14 file = "main.c" +003FD0 TI @19 +003FDC TI @32 +003FE8 TI @124 +003FF4 TI @82 file = "mwlib.c" +004000 TI @100 +00400C TI @105 +004018 TI @111 +004024 TI @125 +004030 TI @136 +00403C TI @256 +004048 TI @33 file = "commands.c" +004054 TI @56 +004060 TI @83 +00406C TI @110 +004078 TI @122 +004084 TI @150 +004090 TI @201 +00409C TI @226 +0040A8 TI @245 +0040B4 TI @272 +0040C0 TI @290 +0040CC TI @317 +0040D8 TI @351 +0040E4 TI @46 file = "copy_attrs.c" +0040F0 TI @7 file = "global_destructor_chain.c" +0040FC TI @2 file = "init_term_dyn.c" +004108 TI @4 +004114 TI @17 file = "start_dyn.c" + +Data section, size = 1084 bytes (TOC anchor = 000000) +000000 TC fs_write_attr file = "*Linker-Generated*" +000004 TC strftime +000008 TC fwrite +00000C TC sprintf +000010 TC open +000014 TC fread +000018 TC fs_fopen_attr_dir +00001C TC fseek +000020 TC strrchr +000024 TC free +000028 TC printf +00002C TC ftell +000030 TC exit +000034 TC strerror +000038 TC __register_fragment +00003C TC memcpy +000040 TC strcmp +000044 TC strlen +000048 TC _call_init_routines_ +00004C TC strdup +000050 TC _files +000054 TC fgets +000058 TC malloc +00005C TC find_thread +000060 TC close +000064 TC memset +000068 TC chmod +00006C TC fopen +000070 TC stat +000074 TC fs_read_attr +000078 TC access +00007C TC fs_read_attr_dir +000080 TC unlink +000084 TC getopt +000088 TC getgid +00008C TC __unregister_fragment +000090 TC _errnop +000094 TC fprintf +000098 TC optind +00009C TC __assertion_failed +0000A0 TC rename +0000A4 TC utime +0000A8 TC getuid +0000AC TC fs_stat_attr +0000B0 TC fflush +0000B4 TC _thread_do_exit_notification +0000B8 TC fclose +0000BC TC localtime +0000C0 TC realloc +0000C4 TC __global_destructor_chain +0000C8 TC __exception_table_end__ +0000CC TC __exception_table_start__ +0000D0 TC __data_end__ +0000D4 TC __data_start__ +0000D8 TC __code_end__ +0000DC TC __code_start__ +0000E0 TC __main_thread_id +0000E4 TC environ +0000E8 TC argv_save +0000EC TC @123 file = "main.c" +0000F0 TC @122 +0000F4 TC @121 +0000F8 TC @120 +0000FC TC @119 +000100 TC @118 +000104 TC @117 +000108 TC @116 +00010C TC @115 +000110 TC @114 +000114 TC @113 +000118 TC @112 +00011C TC @111 +000120 TC @110 +000124 TC @109 +000128 TC @31 +00012C TC @30 +000130 TC @18 +000134 TC @17 +000138 TC @16 +00013C TC @13 +000140 TC @255 file = "mwlib.c" +000144 TC @254 +000148 TC @253 +00014C TC @252 +000150 TC @251 +000154 TC @250 +000158 TC @249 +00015C TC @248 +000160 TC @247 +000164 TC @246 +000168 TC @245 +00016C TC @244 +000170 TC @243 +000174 TC @242 +000178 TC @241 +00017C TC @240 +000180 TC @239 +000184 TC @238 +000188 TC @237 +00018C TC @236 +000190 TC @235 +000194 TC @135 +000198 TC @134 +00019C TC @133 +0001A0 TC @124 +0001A4 TC @123 +0001A8 TC @122 +0001AC TC @104 +0001B0 TC @99 +0001B4 TC @98 +0001B8 TC @81 +0001BC TC @80 +0001C0 TC @79 +0001C4 TC @78 +0001C8 TC @350 file = "commands.c" +0001CC TC @349 +0001D0 TC @348 +0001D4 TC @347 +0001D8 TC @346 +0001DC TC @345 +0001E0 TC @344 +0001E4 TC @343 +0001E8 TC @289 +0001EC TC @288 +0001F0 TC @287 +0001F4 TC @286 +0001F8 TC @285 +0001FC TC @284 +000200 TC @244 +000204 TC @243 +000208 TC @242 +00020C TC @225 +000210 TC @224 +000214 TC @223 +000218 TC @222 +00021C TC @200 +000220 TC @199 +000224 TC @198 +000228 TC @149 +00022C TC @148 +000230 TC @147 +000234 TC @146 +000238 TC @145 +00023C TC @144 +000240 TC @143 +000244 TC @142 +000248 TC @121 +00024C TC @120 +000250 TC @82 +000254 TC @81 +000258 TC @55 +00025C TC @54 +000260 TC @53 +000264 TC @32 +000268 TC @31 +00026C TC @30 +000270 TC @29 +000274 TC @45 file = "copy_attrs.c" +000278 TC @44 +00027C TC @43 +000280 TC magic_template file = "start_dyn.c" +000284 TC default_environ +000288 TD ar_version_id file = "main.c" +00028C TD fragmentID file = "init_term_dyn.c" +000290 DS _term_routine_ file = "init_term_dyn.c" +000298 DS _init_routine_ +0002A0 DS __start file = "start_dyn.c" +0002A8 RW @31 file = "main.c" +0002FC RW @123 +000388 RW @122 +000414 RW magic_template file = "start_dyn.c" +00041C RW @13 +00042C RW default_environ +000434 RW __global_destructor_chain file = "global_destructor_chain.c" + +Import container "libroot.so" +Current Version = 00000000, Old Implementation = 00000000 +000000 DS fs_write_attr +000001 DS strftime +000002 DS fwrite +000003 DS sprintf +000004 DS open +000005 DS fread +000006 DS fs_fopen_attr_dir +000007 DS fseek +000008 DS strrchr +000009 DS free +00000A RW environ +00000B DS printf +00000C DS ftell +00000D DS exit +00000E DS strerror +00000F DS __register_fragment +000010 DS memcpy +000011 DS strcmp +000012 DS strlen +000013 DS _call_init_routines_ +000014 DS strdup +000015 RW argv_save +000016 RW _files +000017 DS fgets +000018 DS malloc +000019 DS find_thread +00001A DS close +00001B DS memset +00001C DS chmod +00001D DS fopen +00001E DS stat +00001F DS fs_read_attr +000020 DS access +000021 DS fs_read_attr_dir +000022 DS unlink +000023 DS getopt +000024 DS getgid +000025 DS __unregister_fragment +000026 DS _errnop +000027 DS fprintf +000028 RW optind +000029 DS __assertion_failed +00002A DS rename +00002B DS utime +00002C RW __main_thread_id +00002D DS getuid +00002E DS fs_stat_attr +00002F DS fflush +000030 DS _thread_do_exit_notification +000031 DS fclose +000032 DS localtime +000033 DS realloc 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; +} diff --git a/BeOS/ar-1.1/commands.h b/BeOS/ar-1.1/commands.h new file mode 100644 index 0000000..e5c28c2 --- /dev/null +++ b/BeOS/ar-1.1/commands.h @@ -0,0 +1,28 @@ +/* +** commands.h - POSIX 1003.2 "ar" command +** +** $Id$ +** +** 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 <be/support/SupportDefs.h> + +status_t do_delete( const char *archive_name, char **files, int verbose ); +status_t do_print( const char *archive_name, char **files, int verbose ); +status_t do_replace( const char *archive_name, char **files, int verbose, + int create, int update ); +status_t do_table( const char *archive_name, char **files, int verbose ); +status_t do_extract( const char *archive_name, char **files, int verobse ); diff --git a/BeOS/ar-1.1/copy_attrs.c b/BeOS/ar-1.1/copy_attrs.c new file mode 100644 index 0000000..c9f978d --- /dev/null +++ b/BeOS/ar-1.1/copy_attrs.c @@ -0,0 +1,128 @@ +/* +** copy_attrs.h - copy BeFS attributes from one file to another +** +** Jan. 11, 1998 Chris Herborth (chrish@qnx.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. +*/ + +#include <support/Errors.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> + +#include "copy_attrs.h" + +static const char *rcs_version_id = "$Id$"; + +/* ---------------------------------------------------------------------- +** Copy file attributes from src_file to dst_file. +*/ + +status_t copy_attrs( const char *dst_file, const char *src_file ) +{ + int dst_fd, src_fd; + status_t retval = B_OK; + DIR *fa_dir = NULL; + struct dirent *fa_ent = NULL; + char *buff = NULL; + struct attr_info fa_info; + off_t read_bytes, wrote_bytes; + + ASSERT( dst_file != NULL ); + ASSERT( src_file != NULL ); + + /* Attempt to open the files. + */ + src_fd = open( src_file, O_RDONLY ); + if( src_fd < 0 ) { + return B_FILE_NOT_FOUND; + } + + dst_fd = open( dst_file, O_WRONLY ); + if( dst_fd < 0 ) { + close( src_fd ); + return B_FILE_NOT_FOUND; + } + + /* Read the attributes, and write them to the destination file. + */ + fa_dir = fs_fopen_attr_dir( src_fd ); + if( fa_dir == NULL ) { + retval = B_IO_ERROR; + goto close_return; + } + + fa_ent = fs_read_attr_dir( fa_dir ); + while( fa_ent != NULL ) { + retval = fs_stat_attr( src_fd, fa_ent->d_name, &fa_info ); + if( retval != B_OK ) { + /* TODO: Print warning message? + */ + goto read_next_attr; + } + + if( fa_info.size > (off_t)UINT_MAX ) { + /* TODO: That's too big. Print a warning message? You could + ** copy it in chunks... + */ + goto read_next_attr; + } + + if( fa_info.size > (off_t)0 ) { + buff = malloc( (size_t)fa_info.size ); + if( buff == NULL ) { + /* TODO: Can't allocate memory for this attribute. Warning? + */ + goto read_next_attr; + } + + read_bytes = fs_read_attr( src_fd, fa_ent->d_name, fa_info.type, + 0, buff, fa_info.size ); + if( read_bytes != fa_info.size ) { + /* TODO: Couldn't read entire attribute. Warning? + */ + goto free_attr_buff; + } + + wrote_bytes = fs_write_attr( dst_fd, fa_ent->d_name, fa_info.type, + 0, buff, fa_info.size ); + if( wrote_bytes != fa_info.size ) { + /* TODO: Couldn't write entire attribute. Warning? + */ + ; + } + + free_attr_buff: + free( buff ); + + retval = B_OK; + } + + /* Read the next entry. + */ + read_next_attr: + fa_ent = fs_read_attr_dir( fa_dir ); + } + +close_return: + close( dst_fd ); + close( src_fd ); + + return retval; +} + diff --git a/BeOS/ar-1.1/copy_attrs.h b/BeOS/ar-1.1/copy_attrs.h new file mode 100644 index 0000000..a761636 --- /dev/null +++ b/BeOS/ar-1.1/copy_attrs.h @@ -0,0 +1,24 @@ +/* +** copy_attrs.h - copy BeFS attributes from one file to another +** +** $Id$ +** +** Jan. 11, 1998 Chris Herborth (chrish@qnx.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. +*/ + +/* ---------------------------------------------------------------------- +** Function prototypes +** +** copy_attrs() - copy BeFS attributes from one file to another +** +** Returns: +** B_OK - all is well +** B_FILE_NOT_FOUND - can't open one of the named files +** B_IO_ERROR - can't read/write some of the file attributes +** B_NO_MEMORY - unable to allocate a buffer for the attribute data +*/ +status_t copy_attrs( const char *dest_file, const char *src_file ); diff --git a/BeOS/ar-1.1/docs/ar.html b/BeOS/ar-1.1/docs/ar.html new file mode 100644 index 0000000..50f002e --- /dev/null +++ b/BeOS/ar-1.1/docs/ar.html @@ -0,0 +1,234 @@ +<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML3.2//EN"> +<!-- $Id$ --> +<html> <head> +<title>ar - create and maintain library archives</title> +</head> + +<body bgcolor="#ffffcb"> +<h1>ar</h1> +<h4 align="right">create and maintain library archives</h4> + +<h2>Synopsis</h2> + +<pre> +ar [-][dprtx][cuv] <i>archive</i> [<i>file</i> ...] +</pre> + +<h2>Options</h2> + +<table> +<tr> + <td valign="top"><b>-</b></td> + <td valign="top"> + The <b>-</b> is optional for introducing <tt>ar</tt> command-line + arguments; this is a POSIX requirement, and I've never seen anyone + use it.</td> +</tr> + +<tr> + <td valign="top"><b>c</b></td> + <td valign="top"> + Don't print a diagnostic message to <i>stderr</i> when + <i>archive</i> is created.</td> +</tr> + +<tr> + <td valign="top"><b>d</b></td> + <td valign="top"> + Delete <i>file(s)</i> from <i>archive</i>.</td> +</tr> + +<tr> + <td valign="top"><b>p</b></td> + <td valign="top"> + Write the contents of the named <i>file(s)</i> to <i>stdout</i>. + If no <i>file(s)</i> are specified, all of the files in + <i>archive</i> are written in the order of the archive.</td> +</tr> + +<tr> + <td valign="top"><b>r</b></td> + <td valign="top"> + Replace or add <i>file(s)</i> to the <i>archive</i>. This will + create <i>archive</i> if it doesn't already exist.</td> +</tr> + +<tr> + <td valign="top"><b>t</b></td> + <td valign="top"> + Write the table of contents of <i>archive</i> to <i>stdout</i>. + If not <i>file(s)</i> are specified, list all of the files, + otherwise only list the specified files.</td> +</tr> + +<tr> + <td valign="top"><b>u</b></td> + <td valign="top"> + Update older files. When used with the <b>r</b> option, files + within the archive are only replaced if <i>file</i> has a + modification date at least as new as the <i>file</i> already in + the archive.</td> +</tr> + +<tr> + <td valign="top"><b>v</b></td> + <td valign="top">Give verbose output.</td> +</tr> + +<tr> + <td valign="top"><b>x</b></td> + <td valign="top"> + Extract <i>file(s)</i> from the <i>archive</i>. If no + <i>file(s)</i> are specified, all of the files in <i>archive</i> + are extracted.</td> +</tr> + +<tr> + <td valign="top"><i>archive</i></td> + <td valign="top"> + The pathname of an archive file.</td> +</tr> + +<tr> + <td valign="top"><i>file</i></td> + <td valign="top"> + One more more pathnames of object files; only the file name is + used when comparing against the names of files in the + archive.</td> +</tr> + +</table> + +<h2>Description</h2> + +<p>The <tt>ar</tt> utility creates and maintains groups of files +combined into a library. Once a library has been created, you can +add new files, and extract, delete, or replace existing files.</p> + +<h2>Exit status</h2> + +<p><tt>ar</tt> exits with one of the following values:</p> + +<table> +<tr><td valign="top">0</td> + <td valign="top">Successful completion.</td></tr> +<tr><td valign="top">> 0</td> + <td>An error occurred.</td></tr> +</table> + +<h2>Bugs</h2> + +<p>No known bugs, but <em>please</em> read the comments in the code if +you want to use it in another application.</p> + +<h2>Comments</h2> + +<p>This is a POSIX 1003.2-1992 based <tt>ar</tt> command; it's not +100% POSIX 1003.2 because POSIX specifies a file format for +<tt>ar</tt> archives. The BeOS <tt>ar</tt> produces library files +compatible (at least in theory <tt>:-)</tt>) with Metrowerks +CodeWarrior for PowerPC.</p> + +<p>This <tt>ar</tt> and its source code were written as a service to +the Be developer community, to make it easier for us to port UNIX +applications and libraries. The code was written from scratch, after +reverse-engineering the Metrowerks library and object file format +(mostly because the library/object file format documentation was +incorrect).</p> + +<p>If you find this useful, please +<a href="mailto:chrish@kagi.com">let me know</a>, and tell me what +you're working on. Be sure to include a URL for your homepage or your +product homepages for my +<a href="http://www.qnx.com/~chrish/Be/community/">Be Community</a> +pages.</p> + +<p>If you find any bugs, please try to fix them, and send me a context +diff (use <tt>diff -c original_file fixed_file</tt>) so I can include +your fixes in the next update. I <i>have</i> tested this, but these +things have a way of slipping though.</p> + +<p>If you'd like to know what other things I'm working on, take a look +at my <a href="http://www.qnx.com/~chrish/Be/software/">Be +Software</a> pages, and my +<a href="http://www.qnx.com/~chrish/Be/">Be Happy!</a> pages.</p> + +<h2>License</h2> + +<p>This program binary and its source code have been donated to the +BeOS Developer Community by Arcane Dragon Software free of charge. +You can do whatever you want with it.</p> + +<p>If you <em>really</em> want to show your appreciation, you could +always send me a gift of some sort; cool software you wrote, nice +pictures for my desktop, ZIP drive disks, RAM, hard drives, post +cards, a pointer to a really cool/useful/interesting web site, +an MPEG audio file of an interesting band (make sure you can give me +enough information to track down their CDs if I like it!), <i>etc.</i> +Send me some <a href="mailto:chrish@kagi.com">email</a> and I'll let you +know where to send it.</p> + +<p>But you don't have to do anything. Just write good BeOS software. +But you're already doing that, right?</p> + +<h2>Disclaimer</h2> + +<p>You use this at your own risk. I've tried to ensure that the code +is correct, but software usually has bugs. If <tt>ar</tt> destroys +your valuable data, formats your hard drive, kicks your cat, and lets +the air out of your tires, I'm not responsible for it. The code is +here, so you should feel fairly safe that there's nothing evil going +on.</p> + +<p>And, as I learned once again in December 1997, you really should +keep backups of everything. I only lost a day's work, but it was +still annoying, and it could've been much, much worse.</p> + +<h3>A word about the code</h3> + +<p>This code isn't meant to be the ultimate in efficiency or speed, +it's intended to be fairly easy to understand and maintain +(hopefully). I was also quite keen on having something that was +correct, without jumping through a lot of unnecessary hoops.</p> + +<p>If you think this code sucks, don't use it. You're already applying +this to your choice of operating system! <tt>:-)</tt></p> + +<h2>Versions</h2> + +<dl compact> + +<dt><strong>1.1 (April 18, 1998)</strong></dt> +<dd>Changes include: + <ul> + <li>Extract option (<b>x</b>) will preserve a file's mode bits + when overwriting an existing file (this may go away if it's + not POSIX behaviour).</li> + + <li>Extracted files will now have the proper file type.</li> + + <li>Removed attempt to use <i>umask()</i> to set newly created + archive's mode bits; apparently, I'm not sure how it + works and my POSIX manual isn't helping.</li> + + <li>Should be 100% endian-neutral now; using this on BeOS for + x86 is only useful if you're manipulating <em>PowerPC</em> + objects though. The <tt>ar</tt> in + <a href="http://www.ninemoons.com/GG/index.html">GeekGadgets</a> + should work fine for x86 objects/libraries.</li> + + <li>Updated the <tt>README.txt</tt> file; now it's got useful + information about building/using the POSIX ar.</li> + </ul></dd> + +<dt><strong>1.0 (January 13, 1998)</strong></dt> +<dd>Initial release.</dd> + +</dl> + +<hr> +<p>Chris Herborth (<a href="mailto:chrish@qnx.com">chrish@qnx.com</a>)</p> +<!-- hhmts start --> +Last modified: $Date$ +<!-- hhmts end --> +</body> </html> diff --git a/BeOS/ar-1.1/docs/dumpar.py b/BeOS/ar-1.1/docs/dumpar.py new file mode 100644 index 0000000..93e2283 --- /dev/null +++ b/BeOS/ar-1.1/docs/dumpar.py @@ -0,0 +1,271 @@ +#! /bin/env python +""" Dump data about a Metrowerks archive file. + +$Id$ + +Based on reverse-engineering the library file format. + +Copyright (C) 1997 Chris Herborth (chrish@qnx.com) +""" + +# ---------------------------------------------------------------------- +# Standard modules +import sys +import getopt +import string +import time + +# ---------------------------------------------------------------------- +def usage(): + """ Display a usage message and exit. + """ + print "dumpar [-v] library1 [library2 ... libraryn]" + print + print "Attempt to display some useful information about the contents" + print "of the given Metrowerks library file(s)." + print + print "-v Be verbose (displays offsets along with the data)" + raise SystemExit + +# ---------------------------------------------------------------------- +def mk_long( str ): + """ convert a 4-byte string into a number + + Assumes big-endian! + """ + if len( str ) < 4: + raise ValueError, "str must be 4 bytes long" + + num = ord( str[3] ) + num = num + ord( str[2] ) * 0x100 + num = num + ord( str[1] ) * 0x10000 + num = num + ord( str[0] ) * 0x1000000 + + return num + +# ---------------------------------------------------------------------- +def str2hex( str ): + """ convert a string into a string of hex numbers + """ + ret = [] + for c in str: + h = hex( ord( c ) ) + ret.append( string.zfill( "%s" % ( h[2:] ), 2 ) ) + + return string.join( ret ) + +# ---------------------------------------------------------------------- +def print_offset( offset ): + """ print the offset nicely + """ + + # Turn the offset into a hex number and strip off the leading "0x". + val = "%s" % ( hex( offset ) ) + val = val[2:] + + out = "0x" + string.zfill( val, 8 ) + + print out, + +# ---------------------------------------------------------------------- +def get_string( data ): + """ dig a C string out of a data stream + + returns the string + """ + len = 0 + while data[len] != '\0': + len = len + 1 + + return data[:len] + +# ---------------------------------------------------------------------- +def dump_lib( file, verbose ): + """ dump information about a Metrowerks library file + """ + offset = 0 + + print "Dumping library:", file + + # Attempt to read the data. + try: + data = open( file ).read() + except IOError, retval: + print "*** Unable to open file %s: %s" % ( file, retval[1] ) + return + + # Check the magic number. + if verbose: + print_offset( offset ) + print "Magic:", + magic = data[offset:offset + 8] + print "'%s'" % ( magic ) + if magic != "MWOBPPC ": + print "*** Invalid magic number!" + return + + offset = offset + 8 + + # File flags + if verbose: + print_offset( offset ) + print "file flags:", + print mk_long( data[offset:offset + 4] ) + offset = offset + 4 + + if verbose: + print_offset( offset ) + print "file version:", + print mk_long( data[offset:offset + 4] ) + offset = offset + 4 + + # code size + if verbose: + print_offset( offset ) + print "code size:", mk_long( data[offset:offset + 4] ) + offset = offset + 4 + + # data size + if verbose: + print_offset( offset ) + print "data size:", mk_long( data[offset:offset + 4] ) + offset = offset + 4 + + # number of objects + if verbose: + print_offset( offset ) + print "number of objects:", + num_objs = mk_long( data[offset:offset + 4] ) + print num_objs + + offset = offset + 4 + + print + + # Now loop through the objects. + obj_sizes = [ 0, ] * num_objs + obj_data_offsets = [ 0, ] * num_objs + + for obj in range( num_objs ): + # Magic? + if verbose: + print_offset( offset ) + print "modification time:", + modtime = mk_long( data[offset:offset + 4] ) + print "[%s]" % ( ( time.localtime( modtime ), ) ) + + offset = offset + 4 + + # Offsets? + if verbose: + print_offset( offset ) + print "file name offset 1:", + file_offset1 = mk_long( data[offset:offset + 4] ) + unknown = "%s" % ( hex( file_offset1 ) ) + print "%s (%s)" % ( unknown, str2hex( data[offset:offset + 4] ) ) + + offset = offset + 4 + + if verbose: + print_offset( offset ) + print "file name offset 2:", + file_offset2 = mk_long( data[offset:offset + 4] ) + unknown = "%s" % ( hex( file_offset2 ) ) + print "%s (%s)" % ( unknown, str2hex( data[offset:offset + 4] ) ) + + offset = offset + 4 + + # Extra -1 for NUL character. + print " >>>> File name should be %s characters." % \ + ( file_offset2 - file_offset1 - 1) + + if verbose: + print_offset( offset ) + print "object data offset:", + file_data_offset = mk_long( data[offset:offset + 4] ) + unknown = "%s" % ( hex( file_data_offset ) ) + print "%s (%s)" % ( unknown, str2hex( data[offset:offset + 4] ) ) + + obj_data_offsets[obj] = file_data_offset + + offset = offset + 4 + + # object size + if verbose: + print_offset( offset ) + print "object size:", + obj_sizes[obj] = mk_long( data[offset:offset + 4] ) + print "%s bytes" % ( obj_sizes[obj] ) + + offset = offset + 4 + + print + + # Now loop through the object names. + for obj in range( num_objs ): + # First name + if verbose: + print_offset( offset ) + print "object", + print obj, + print "name 1:", + name1 = get_string( data[offset:] ) + print "[%s] %s chars" % ( name1, len( name1 ) ) + + offset = offset + len( name1 ) + 1 + + # Second name + if verbose: + print_offset( offset ) + print "object", + print obj, + print "name 2:", + name2 = get_string( data[offset:] ) + print "[%s] %s chars" % ( name2, len( name1 ) ) + + offset = offset + len( name2 ) + 1 + + # See if we've got a magic cookie in the object data + if verbose: + print_offset( obj_data_offsets[obj] ) + + cookie = data[obj_data_offsets[obj]:obj_data_offsets[obj] + 8] + print "object", + print obj, + print "cookie: '%s'" % ( cookie ) + + print + + # Now loop through the data and check for magic numbers there. + return + +# ---------------------------------------------------------------------- +def main(): + """ mainline + """ + + # Set up some defaults + be_verbose = 0 + + # First, check the command-line arguments + try: + opt, args = getopt.getopt( sys.argv[1:], "vh?" ) + except getopt.error: + print "*** Error parsing command-line options!" + usage() + + for o in opt: + if o[0] == "-h" or o[0] == "-?": + usage() + elif o[0] == "-v": + be_verbose = 1 + else: + print "*** Unknown command-line option!" + usage() + + # Now we can attempt to dump info about the arguments. + for lib in args: + dump_lib( lib, be_verbose ) + +if __name__ == "__main__": + main() diff --git a/BeOS/ar-1.1/docs/dumpar.pyc b/BeOS/ar-1.1/docs/dumpar.pyc Binary files differnew file mode 100644 index 0000000..bc5045f --- /dev/null +++ b/BeOS/ar-1.1/docs/dumpar.pyc diff --git a/BeOS/ar-1.1/docs/dumpo.py b/BeOS/ar-1.1/docs/dumpo.py new file mode 100644 index 0000000..91bd8db --- /dev/null +++ b/BeOS/ar-1.1/docs/dumpo.py @@ -0,0 +1,126 @@ +#! /bin/env python +""" Dump data about a Metrowerks object file. + +Based on reverse-engineering the library file format, since the docs are +wrong. + +Copyright (C) 1997 Chris Herborth (chrish@qnx.com) +""" + +# ---------------------------------------------------------------------- +# Standard modules +import sys, getopt, string, time + +# ---------------------------------------------------------------------- +# Extra goodies +from dumpar import mk_long, str2hex, print_offset, get_string + +# ---------------------------------------------------------------------- +def mk_short( str ): + """ convert a 2-byte string into a number + + Assumes big-endian! + """ + if len( str ) < 2: + raise ValueError, "str must be 2 bytes long" + + num = ord( str[1] ) + num = num + ord( str[0] ) * 0x100 + + return num + +# ---------------------------------------------------------------------- +def usage(): + """ Display a usage message and exit. + """ + print "dumpo [-v] object1 [object2 ... objectn]" + print + print "Attempt to display some useful information about the contents" + print "of the given Metrowerks object file(s)." + print + print "-v Be verbose (displays offsets along with the data)" + raise SystemExit + +# ---------------------------------------------------------------------- +def dump_o( file, verbose ): + """ dump information about a Metrowerks object file + + Note that there is more info there, 6 more quads before the file name. + """ + offset = 0 + + print "Dumping object:", file + + # Attempt to read the data. + try: + data = open( file ).read() + except IOError, retval: + print "*** Unable to open file %s: %s" % ( file, retval[1] ) + return + + # Check the magic number. + if verbose: + print_offset( offset ) + print "Magic:", + magic = data[offset:offset + 8] + print "'%s'" % ( magic ) + if magic != "MWOBPPC ": + print "*** Invalid magic number!" + return + + offset = offset + 8 + + # version + if verbose: + print_offset( offset ) + print "version:", mk_long( data[offset:offset + 4] ) + offset = offset + 4 + + # flags + if verbose: + print_offset( offset ) + print "flags:", str2hex( data[offset:offset + 4] ) + offset = offset + 4 + + # code size + if verbose: + print_offset( offset ) + print "code size:", mk_long( data[offset:offset + 4] ) + offset = offset + 4 + + # data size + if verbose: + print_offset( offset ) + print "data size:", mk_long( data[offset:offset + 4] ) + offset = offset + 4 + +# ---------------------------------------------------------------------- +def main(): + """ mainline + """ + + # Set up some defaults + be_verbose = 0 + + # First, check the command-line arguments + try: + opt, args = getopt.getopt( sys.argv[1:], "vh?" ) + except getopt.error: + print "*** Error parsing command-line options!" + usage() + + for o in opt: + if o[0] == "-h" or o[0] == "-?": + usage() + elif o[0] == "-v": + be_verbose = 1 + else: + print "*** Unknown command-line option!" + usage() + + # Now we can attempt to dump info about the arguments. + for obj in args: + dump_o( obj, be_verbose ) + +if __name__ == "__main__": + main() diff --git a/BeOS/ar-1.1/docs/notes b/BeOS/ar-1.1/docs/notes new file mode 100644 index 0000000..4a90a16 --- /dev/null +++ b/BeOS/ar-1.1/docs/notes @@ -0,0 +1,34 @@ +MW library layout: + +header + magic word, magic processor flag ('MWOBPPC ') - 2x 4 bytes + magic flags, version (file format version?) - 2x 4 bytes + code size - 4 bytes + data size - 4 bytes + # of objects - 4 bytes + + header for file 1 - 20 bytes + - modification time - 4 bytes + - offset to filename - 4 bytes + - offset to full path - 4 bytes (NOTE: NOT a full path in reality!) + - offset to object data - 4 bytes + - size of object data - 4 bytes + + ... + + header for file n - 20 bytes + + file 1 name + NUL - variable + file 1 name + NUL - variable + file 2 name + NUL - variable + file 2 name + NUL - variable + ... + file n name + NUL - variable + file n name + NUL - variable + + padding to multiple of 4 bytes - 0 - 3 bytes + +file 1 data - variable (padded to 4-byte boundary) +file 2 data - variable (padded to 4-byte boundary) +... +file n data - variable (padded to 4-byte boundary) diff --git a/BeOS/ar-1.1/main.c b/BeOS/ar-1.1/main.c new file mode 100644 index 0000000..225ee62 --- /dev/null +++ b/BeOS/ar-1.1/main.c @@ -0,0 +1,312 @@ +/* +** main.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 <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <getopt.h> + +#include "commands.h" + +static const char *rcs_version_id = "$Id$"; +static const char *ar_version_id = "1.0 " __DATE__; + +/* ---------------------------------------------------------------------- */ +typedef enum { + delete_cmd, + print_cmd, + replace_cmd, + table_cmd, + extract_cmd, + no_cmd = -1 } command; + +/* ---------------------------------------------------------------------- +** Prototypes +*/ +void usage( void ); +void version( void ); +void check_command( command *cmd, int arg ); + +/* ---------------------------------------------------------------------- +** Print a usage message and exit. +*/ +void usage( void ) +{ + printf( "ar [dprtx][cuv] archive [file ...]\n" ); + + exit( EXIT_FAILURE ); +} + +/* ---------------------------------------------------------------------- +** Print a version message and exit. +*/ +void version( void ) +{ + printf( "ar (POSIX 1003.2-1992), version %s\n", ar_version_id ); + printf( "by Chris Herborth (chrish@qnx.com)\n" ); + printf( "This code has been donated to the BeOS developer community.\n" ); + + return; +} + +/* ---------------------------------------------------------------------- +** Set *cmd to the appropriate command enum if it isn't already set. +*/ +void check_command( command *cmd, int arg ) +{ + if( *cmd == no_cmd ) { + switch( arg ) { + case 'd': + *cmd = delete_cmd; + break; + case 'p': + *cmd = print_cmd; + break; + case 'r': + *cmd = replace_cmd; + break; + case 't': + *cmd = table_cmd; + break; + case 'x': + *cmd = extract_cmd; + break; + } + } else { + printf( "ar: you can only specify one command at a time\n" ); + usage(); + } +} + +/* ---------------------------------------------------------------------- +** Mainline +*/ +int main( int argc, char **argv ) +{ + command cmd = no_cmd; + int verbose_flag = 0; + int create_flag = 0; /* these two only apply to replace_cmd */ + int update_flag = 0; + int c = 0; + + char *archive_name; + char **files_list; + int num_files; + + int idx; + status_t retval; + + /* The argument parsing is a little hairier than usual; the idea is + ** to support the POSIX 1003.2 style of arguments, and the much more + ** common traditional argument style. + */ + if( argc < 3 ) { + printf( "ar: invalid number of arguments\n" ); + usage(); + } + + /* Do we have traditional or POSIX-style args? */ + if( argv[1][0] == '-' ) { + while( ( c = getopt( argc, argv, "dprtxcuvV" ) ) != EOF ) { + switch( c ) { + case 'd': /* fall-through */ + case 'p': /* fall-through */ + case 'r': /* fall-through */ + case 't': /* fall-through */ + case 'x': /* fall-through */ + check_command( &cmd, c ); + break; + + case 'v': + verbose_flag = 1; + break; + + case 'c': + if( cmd != no_cmd && cmd != replace_cmd ) { + printf( "ar: invalid option, -c\n" ); + usage(); + } else { + create_flag = 1; + } + break; + + case 'u': + if( cmd != no_cmd && cmd != replace_cmd ) { + printf( "ar: invalid option, -u\n" ); + usage(); + } else { + update_flag = 1; + } + break; + + case 'V': + version(); + break; + + default: + printf( "ar: invalid option, -%c\n", c ); + usage(); + break; + } + + idx = optind; + } + } else { + /* In the traditional way, arguments ar: + ** + ** argv[1] = [dprtx][cuv] + ** argv[2] = archive + ** argv[...] = file ... + **/ + char *ptr; + + idx = 1; + + ptr = argv[idx++]; + + while( *ptr != '\0' ) { + switch( *ptr ) { + case 'd': /* fall-through */ + case 'p': /* fall-through */ + case 'r': /* fall-through */ + case 't': /* fall-through */ + case 'x': /* fall-through */ + check_command( &cmd, *ptr ); + break; + + case 'v': + verbose_flag = 1; + break; + + case 'c': + if( cmd != no_cmd && cmd != replace_cmd ) { + printf( "ar: invalid option, -c\n" ); + usage(); + } else { + create_flag = 1; + } + break; + + case 'u': + if( cmd != no_cmd && cmd != replace_cmd ) { + printf( "ar: invalid option, -u\n" ); + usage(); + } else { + update_flag = 1; + } + break; + + case 'V': + version(); + break; + + default: + printf( "ar: invalid option, -%c\n", c ); + usage(); + break; + } + + ptr++; + } + } + + /* Next arg is the archive. */ + archive_name = argv[idx++]; + + /* Next are the files. */ + num_files = argc - idx; + + if( num_files == 0 ) { + files_list = NULL; + } else { + int ctr = 0; + + files_list = (char **)malloc( ( num_files + 1 ) * sizeof( char * ) ); + + while( idx < argc ) { + files_list[ctr++] = argv[idx++]; + } + + files_list[idx] = NULL; + } + + /* Now we can attempt to manipulate the archive. */ + switch( cmd ) { + case delete_cmd: + retval = do_delete( archive_name, files_list, verbose_flag ); + break; + + case print_cmd: + retval = do_print( archive_name, files_list, verbose_flag ); + break; + + case replace_cmd: + retval = do_replace( archive_name, files_list, verbose_flag, + create_flag, update_flag ); + break; + + case table_cmd: + retval = do_table( archive_name, files_list, verbose_flag ); + break; + + case extract_cmd: + retval = do_extract( archive_name, files_list, verbose_flag ); + break; + + default: + printf( "ar: you must specify a command\n" ); + usage(); + break; + } + + /* Check the return value. + */ + switch( retval ) { + case B_OK: + break; + case B_FILE_NOT_FOUND: + printf( "can't open the file %s\n", archive_name ); + return EXIT_FAILURE; + break; + case B_IO_ERROR: + printf( "can't read from %s\n", archive_name ); + return EXIT_FAILURE; + break; + case B_BAD_VALUE: + printf( "invalid magic word\n" ); + return EXIT_FAILURE; + break; + case B_MISMATCHED_VALUES: + printf( "invalid processor value, or magicflags, or version\n" ); + return EXIT_FAILURE; + break; + case B_NO_MEMORY: + printf( "unable to allocate memory\n" ); + return EXIT_FAILURE; + break; + case B_ERROR: + printf( "error during processing\n" ); + return EXIT_FAILURE; + default: + printf( "unknown error: %ld\n", retval ); + return EXIT_FAILURE; + break; + } + + return EXIT_SUCCESS; +} 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; +} diff --git a/BeOS/ar-1.1/mwlib.h b/BeOS/ar-1.1/mwlib.h new file mode 100644 index 0000000..67af325 --- /dev/null +++ b/BeOS/ar-1.1/mwlib.h @@ -0,0 +1,118 @@ +/* +** mwlib.h - POSIX 1003.2 "ar" command +** +** $Id$ +** +** 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@qnx.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/SupportDefs.h> +#include <time.h> + +/* ---------------------------------------------------------------------- +** Constants +** +*/ +#define MWLIB_MAGIC_WORD 'MWOB' +#define MWLIB_MAGIC_PROC 'PPC ' + +/* ---------------------------------------------------------------------- +** Structures +** +** This is based on the "Metrowerks CodeWarrior Library Reference +** Specification", which isn't 100% accurate for BeOS. +*/ + +typedef struct MWLibHeader { + uint32 magicword; + uint32 magicproc; + uint32 magicflags; + uint32 version; + + uint32 code_size; + uint32 data_size; + + uint32 num_objects; +} MWLibHeader; + +typedef struct MWLibFile { + time_t m_time; + + uint32 off_filename; + uint32 off_fullpath; + uint32 off_object; + uint32 object_size; +} MWLibFile; + +typedef struct MWLib { + MWLibHeader header; + + MWLibFile *files; + + char **names; + + char **data; +} MWLib; + +/* This bears no resemblance to what's in the Metrowerks docs. +** +** Note that this is incomplete; this is all the info I needed for +** ar though. +*/ +typedef struct MWObject { + uint32 magic_word; /* 'MWOB' */ + uint32 arch; /* 'PPC '; this isn't in the docs */ + uint32 version; + uint32 flags; + + uint32 code_size; + uint32 data_size; +} MWObject; + +/* ---------------------------------------------------------------------- +** Function prototypes +** +** load_MW_lib() - load a Metrowerks library +** +** 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 +** +** write_MW_lib() - write a Metrowerks library +** +** Returns: +** B_OK - all is well +** +** write_MW_lib() - 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. +*/ +status_t load_MW_lib( MWLib *lib, const char *filename ); +status_t write_MW_lib( MWLib *lib, const char *filename ); +void setfiletype( const char *filename, const char *type ); |