blob: f3b86605fd7992e5ad85fe00efaf1d8f45c057c2 [file] [log] [blame]
Guido van Rossumd8eb2111998-08-04 17:57:28 +00001/*
2** mwlib.c - POSIX 1003.2 "ar" command
3**
4** This isn't a pure POSIX 1003.2 ar; it only manipulates Metrowerks
5** Library files, not general-purpose POSIX 1003.2 format archives.
6**
7** Dec. 14, 1997 Chris Herborth (chrish@kagi.com)
8**
9** This code is donated to the PUBLIC DOMAIN. You can use, abuse, modify,
10** redistribute, steal, or otherwise manipulate this code. No restrictions
11** at all. If you laugh at this code, you can't use it.
12**
13** This "ar" was implemented using IEEE Std 1003.2-1992 as the basis for
14** the interface, and Metrowerk's published docs detailing their library
15** format. Look inside for clues about how reality differs from MW's
16** documentation on BeOS...
17*/
18
19#include <support/Errors.h>
20#include <support/byteorder.h>
21#ifndef NO_DEBUG
22#include <assert.h>
23#define ASSERT(cond) assert(cond)
24#else
25#define ASSERT(cond) ((void)0)
26#endif
27
28#include <stdio.h>
29#include <stdlib.h>
30#include <unistd.h>
31#include <limits.h>
32#include <string.h>
33#include <errno.h>
34#include <kernel/fs_attr.h>
35#include <fcntl.h> /* is open() really here?!? sheesh... */
36#include <storage/Mime.h>
37#include <sys/stat.h>
38
39#include "mwlib.h"
40#include "copy_attrs.h"
41
42static const char *rcs_version_id = "$Id$";
43
44/* ----------------------------------------------------------------------
45** Local prototypes
46*/
47static status_t load_MWLibFile( FILE *file, MWLibFile *libfile );
48static size_t fwrite_big32( uint32 x, FILE *fp );
49static size_t fwrite_big32_seek( uint32 x, long offset, FILE *fp );
50static status_t add_object_sizes( MWObject *obj, uint32 *code, uint32 *data );
51
52/* ----------------------------------------------------------------------
53** Load a Metrowerks library file into the given MWLib object.
54**
55** Returns:
56** B_OK - all is well
57** B_FILE_NOT_FOUND - can't open the given file
58** B_IO_ERROR - can't read from the given file
59** B_BAD_VALUE - invalid magic word in the file
60** B_MISMATCHED_VALUES - invalid processor value (ie, not a PowerPC lib),
61** or the magicflags member is not 0, or the file
62** version number isn't 1.
63** B_NO_MEMORY - unable to allocate memory while loading the lib
64*/
65status_t load_MW_lib( MWLib *lib, const char *filename )
66{
67 FILE *fp = NULL;
68 size_t recs = 0;
69 status_t retval = B_OK;
70 uint32 tmp32 = 0;
71 uint32 idx = 0;
72 char obj_name[PATH_MAX];
73
74 ASSERT( lib != NULL );
75 ASSERT( filename != NULL );
76
77 fp = fopen( filename, "r" );
78 if( !fp ) {
79 return B_FILE_NOT_FOUND;
80 }
81
82 /* Read and check the magic number.
83 */
84 recs = fread( &tmp32, sizeof( uint32 ), 1, fp );
85 lib->header.magicword = B_BENDIAN_TO_HOST_INT32( tmp32 );
86 if( recs != 1 ) {
87 retval = B_IO_ERROR;
88 goto close_return;
89 } else if( lib->header.magicword != MWLIB_MAGIC_WORD ) {
90 retval = B_BAD_VALUE;
91 goto close_return;
92 }
93
94 /* Read and check the processor.
95 */
96 recs = fread( &tmp32, sizeof( uint32 ), 1, fp );
97 lib->header.magicproc = B_BENDIAN_TO_HOST_INT32( tmp32 );
98 if( recs != 1 ) {
99 retval = B_IO_ERROR;
100 goto close_return;
101 } else if( lib->header.magicproc != MWLIB_MAGIC_PROC ) {
102 retval = B_MISMATCHED_VALUES;
103 goto close_return;
104 }
105
106 /* Read and check the flags.
107 */
108 recs = fread( &tmp32, sizeof( uint32 ), 1, fp );
109 lib->header.magicflags = B_BENDIAN_TO_HOST_INT32( tmp32 );
110 if( recs != 1 ) {
111 retval = B_IO_ERROR;
112 goto close_return;
113 } else if( lib->header.magicflags != 0 ) {
114 retval = B_MISMATCHED_VALUES;
115 goto close_return;
116 }
117
118 /* Read and check the file version.
119 */
120 recs = fread( &tmp32, sizeof( uint32 ), 1, fp );
121 lib->header.version = B_BENDIAN_TO_HOST_INT32( tmp32 );
122 if( recs != 1 ) {
123 retval = B_IO_ERROR;
124 goto close_return;
125 } else if( lib->header.version != 1 ) {
126 retval = B_MISMATCHED_VALUES;
127 goto close_return;
128 }
129
130 /* Read the code size, data size, and number of objects.
131 */
132 recs = fread( &tmp32, sizeof( uint32 ), 1, fp );
133 lib->header.code_size = B_BENDIAN_TO_HOST_INT32( tmp32 );
134 if( recs != 1 ) {
135 retval = B_IO_ERROR;
136 goto close_return;
137 }
138
139 recs = fread( &tmp32, sizeof( uint32 ), 1, fp );
140 lib->header.data_size = B_BENDIAN_TO_HOST_INT32( tmp32 );
141 if( recs != 1 ) {
142 retval = B_IO_ERROR;
143 goto close_return;
144 }
145
146 recs = fread( &tmp32, sizeof( uint32 ), 1, fp );
147 lib->header.num_objects = B_BENDIAN_TO_HOST_INT32( tmp32 );
148 if( recs != 1 ) {
149 retval = B_IO_ERROR;
150 goto close_return;
151 }
152
153 /* Load the MWLibFile objects from the lib.
154 */
155 lib->files = (MWLibFile *)malloc( lib->header.num_objects * sizeof( MWLibFile ) );
156 if( lib->files == NULL ) {
157 retval = B_NO_MEMORY;
158 goto close_return;
159 }
160
161 for( idx = 0; idx < lib->header.num_objects; idx++ ) {
162 retval = load_MWLibFile( fp, &(lib->files[idx]) );
163 if( retval != B_OK ) {
164 goto close_return;
165 }
166 }
167
168 /* Load the file names and object data.
169 **
170 ** The file name actually appears twice in the library file; according
171 ** to the docs, one is the file name, the other is the "full path name".
172 ** In all of the libraries on my system, they're the same.
173 */
174 lib->names = (char **)malloc( lib->header.num_objects * sizeof( char * ) );
175 lib->data = (char **)malloc( lib->header.num_objects * sizeof( char * ) );
176 if( lib->names == NULL || lib->data == NULL ) {
177 retval = B_NO_MEMORY;
178 goto close_return;
179 }
180
181 for( idx = 0; idx < lib->header.num_objects; idx ++ ) {
182 /* Load the name and copy it into the right spot.
183 */
184 retval = fseek( fp, lib->files[idx].off_filename, SEEK_SET );
185 if( retval ) {
186 retval = B_IO_ERROR;
187 goto close_return;
188 }
189
190 if( fgets( obj_name, PATH_MAX, fp ) == NULL ) {
191 retval = B_IO_ERROR;
192 goto close_return;
193 }
194
195 lib->names[idx] = strdup( obj_name );
196 if( lib->names[idx] == NULL ) {
197 retval = B_NO_MEMORY;
198 goto close_return;
199 }
200
201 /* Load the object data.
202 */
203 lib->data[idx] = (char *)malloc( lib->files[idx].object_size );
204 if( lib->data[idx] == NULL ) {
205 retval = B_NO_MEMORY;
206 goto close_return;
207 }
208
209 retval = fseek( fp, lib->files[idx].off_object, SEEK_SET );
210 if( retval ) {
211 retval = B_IO_ERROR;
212 goto close_return;
213 }
214
215 recs = fread( lib->data[idx], lib->files[idx].object_size, 1, fp );
216 if( recs != 1 ) {
217 retval = B_IO_ERROR;
218 goto close_return;
219 }
220 }
221
222close_return:
223 fclose( fp );
224 return retval;
225}
226
227/* ----------------------------------------------------------------------
228** Load the file header from a Metrowerks library file.
229**
230** Returns:
231** B_OK - All is well
232** B_IO_ERROR - Error reading the file
233*/
234static status_t load_MWLibFile( FILE *file, MWLibFile *libfile )
235{
236 size_t recs = 0;
237 uint32 tmp32 = 0;
238
239 ASSERT( file != NULL );
240 ASSERT( libfile != NULL );
241
242 /* Load the modification time.
243 */
244 recs = fread( &tmp32, sizeof( uint32 ), 1, file );
245 libfile->m_time = (time_t)B_BENDIAN_TO_HOST_INT32( tmp32 );
246 if( recs != 1 ) {
247 return B_IO_ERROR;
248 }
249
250 /* Load the various offsets.
251 */
252 recs = fread( &tmp32, sizeof( uint32 ), 1, file );
253 libfile->off_filename = B_BENDIAN_TO_HOST_INT32( tmp32 );
254 if( recs != 1 ) {
255 return B_IO_ERROR;
256 }
257
258 recs = fread( &tmp32, sizeof( uint32 ), 1, file );
259 libfile->off_fullpath = B_BENDIAN_TO_HOST_INT32( tmp32 );
260 if( recs != 1 ) {
261 return B_IO_ERROR;
262 }
263
264 recs = fread( &tmp32, sizeof( uint32 ), 1, file );
265 libfile->off_object = B_BENDIAN_TO_HOST_INT32( tmp32 );
266 if( recs != 1 ) {
267 return B_IO_ERROR;
268 }
269
270 /* Load the object size.
271 */
272 recs = fread( &tmp32, sizeof( uint32 ), 1, file );
273 libfile->object_size = B_BENDIAN_TO_HOST_INT32( tmp32 );
274 if( recs != 1 ) {
275 return B_IO_ERROR;
276 }
277
278 return B_OK;
279}
280
281/* ----------------------------------------------------------------------
282** Write a Metrowerks library file.
283**
284** Returns:
285** B_OK - all is well; doesn't necessarily mean the file was written
286** properly, just that you didn't lose any data
287** B_NO_MEMORY - unable to allocate offset buffers
288** B_ERROR - problem with backup file (can't rename existing file)
289**
290** Note:
291** If you use this in a long-lived program, it leaks memory; the MWLib
292** contents are never free()'d.
293**
294** Two-pass technique:
295**
296** pass 1 - write file header (need CODE SIZE and DATA SIZE)
297** write object headers (need offsets for FILENAME, FULL PATH, DATA)
298** write filenames (store offsets for object headers)
299** write padding (file size % 4 bytes)
300** write file data (store offset for object header; add to code/data
301** size for file header; pad data size % 4 bytes)
302**
303** pass 2 - fill in file header CODE SIZE and DATA SIZE
304** fill in object headers FILENAME, FULL PATH, DATA
305**
306** You could avoid this by building up the file headers in memory, but this is
307** easier (?) and I'm not that concerned with speed...
308**
309*/
310
311typedef struct file_header_offsets {
312 long codesize_offset;
313 long datasize_offset;
314} file_header_offsets;
315
316typedef struct obj_header_offsets {
317 long filename_offset;
318 uint32 filename_offset_value;
319
320 long fullpath_offset;
321 uint32 fullpath_offset_value;
322
323 long data_offset;
324 uint32 data_offset_value;
325} obj_header_offsets;
326
327static size_t fwrite_big32( uint32 x, FILE *fp )
328{
329 uint32 tmp32 = B_HOST_TO_BENDIAN_INT32( x );
330
331 ASSERT( fp != NULL );
332
333 return fwrite( &tmp32, sizeof(tmp32), 1, fp );
334}
335
336static size_t fwrite_big32_seek( uint32 x, long offset, FILE *fp )
337{
338 uint32 tmp32 = B_HOST_TO_BENDIAN_INT32( x );
339
340 ASSERT( fp != NULL );
341
342 if( fseek( fp, offset, SEEK_SET ) ) {
343 return 0;
344 }
345
346 return fwrite( &tmp32, sizeof(tmp32), 1, fp );
347}
348
349static status_t add_object_sizes( MWObject *obj, uint32 *code, uint32 *data )
350{
351 ASSERT( obj != NULL );
352 ASSERT( code != NULL );
353 ASSERT( data != NULL );
354
355 if( B_BENDIAN_TO_HOST_INT32( obj->magic_word ) != 'MWOB' ||
356 B_BENDIAN_TO_HOST_INT32( obj->arch ) != 'PPC ' ) {
357 return B_ERROR;
358 }
359
360 *code += B_BENDIAN_TO_HOST_INT32( obj->code_size );
361 *data += B_BENDIAN_TO_HOST_INT32( obj->data_size );
362
363 return B_OK;
364}
365
366void setfiletype( const char *file, const char *type )
367{
368 int fd;
369 attr_info fa;
370 ssize_t wrote_bytes;
371
372 fd = open( file, O_RDWR );
373 if( fd < 0 ) {
374 fprintf( stderr, "ar: can't open %s to write file type, %s",
375 file, strerror( errno ) );
376 return;
377 }
378
379 fa.type = B_MIME_STRING_TYPE;
380 fa.size = (off_t)(strlen( type ) + 1);
381
382 wrote_bytes = fs_write_attr( fd, "BEOS:TYPE", fa.type, 0,
383 type, fa.size );
384 if( wrote_bytes != (ssize_t)fa.size ) {
385 fprintf( stderr, "ar: couldn't write complete file type, %s",
386 strerror( errno ) );
387 }
388
389 close( fd );
390}
391
392status_t write_MW_lib( MWLib *lib, const char *filename )
393{
394 char *padding = "\0\0\0\0";
395 long pad_amount;
396
397 file_header_offsets head_offs;
398 obj_header_offsets *obj_offs;
399 uint32 codesize = 0;
400 uint32 datasize = 0;
401 uint32 num_objects = 0;
402
403 FILE *fp;
404 char *tmp_filename = NULL;
405 uint32 idx = 0;
406 size_t recs;
407 status_t retval;
408
409 mode_t mode_bits = 0666;
410
411 ASSERT( lib != NULL );
412 ASSERT( filename != NULL );
413
414#if 0
415/* Apparently, I don't understand umask()... */
416
417 /* Find the default file creation mask. The semantics of umask() suck.
418 */
419 mode_bits = umask( (mode_t)0 );
420 (void)umask( mode_bits );
421#endif
422
423 /* Easier access to the number of objects.
424 */
425 num_objects = lib->header.num_objects;
426
427 /* Allocate some storage for keeping track of the things we need to
428 ** write out in the second pass.
429 */
430 head_offs.codesize_offset = 0;
431 head_offs.datasize_offset = 0;
432
433 obj_offs = (obj_header_offsets *)malloc( sizeof(obj_header_offsets) *
434 num_objects );
435 if( obj_offs == NULL ) {
436 fprintf( stderr, "ar: can't allocate memory for writing file\n" );
437
438 return B_NO_MEMORY;
439 }
440 memset( obj_offs, 0, sizeof(obj_header_offsets) * num_objects );
441
442 /* If the file exists, move it somewhere so we can recover if there's
443 ** an error.
444 */
445 retval = access( filename, F_OK );
446 if( retval == 0 ) {
447 struct stat s;
448
449 /* Preserve the mode bits.
450 */
451 retval = stat( filename, &s );
452 if( retval != 0 ) {
453 fprintf( stderr, "ar: can't stat %s, %s\n", filename,
454 strerror( errno ) );
455 } else {
456 mode_bits = s.st_mode;
457 }
458
459 tmp_filename = (char *)malloc( strlen( filename ) + 2 );
460 if( tmp_filename == NULL ) {
461 fprintf( stderr,
462 "ar: can't allocate memory for temporary filename\n" );
463 } else {
464 sprintf( tmp_filename, "%s~", filename );
465
466 retval = access( tmp_filename, F_OK );
467 if( retval == 0 ) {
468 retval = unlink( tmp_filename );
469 if( retval != 0 ) {
470 fprintf( stderr, "ar: can't unlink %s, %s\n", tmp_filename,
471 strerror( errno ) );
472
473 free( tmp_filename );
474 tmp_filename = NULL;
475 }
476 }
477
478 if( tmp_filename ) {
479 retval = rename( filename, tmp_filename );
480 if( retval != 0 ) {
481 fprintf( stderr, "ar: can't move %s to backup, %s\n",
482 filename, strerror( errno ) );
483
484 return B_ERROR;
485 }
486 }
487 }
488 }
489
490 /* Attempt to open the archive file.
491 */
492 fp = fopen( filename, "w" );
493 if( fp == NULL ) {
494 fprintf( stderr, "ar: can't open %s for write, %s\n", filename,
495 strerror( errno ) );
496 goto error_return;
497 }
498
499 /* ----------------------------------------------------------------------
500 ** Write the file. Pass 1.
501 */
502 recs = fwrite_big32( lib->header.magicword, fp );
503 recs += fwrite_big32( lib->header.magicproc, fp );
504 recs += fwrite_big32( lib->header.magicflags, fp );
505 recs += fwrite_big32( lib->header.version, fp );
506 if( recs != 4 ) {
507 fprintf( stderr, "ar: error writing header for %s, %s\n", filename,
508 strerror( errno ) );
509 goto error_return;
510 }
511
512 /* Keep track of the code/data size offsets.
513 */
514 head_offs.codesize_offset = ftell( fp );
515 recs = fwrite_big32( codesize, fp );
516 head_offs.datasize_offset = ftell( fp );
517 recs += fwrite_big32( datasize, fp );
518 if( recs != 2 ) {
519 fprintf( stderr, "ar: error writing header for %s, %s\n", filename,
520 strerror( errno ) );
521 goto error_return;
522 }
523
524 recs = fwrite_big32( num_objects, fp );
525 if( recs != 1 ) {
526 fprintf( stderr, "ar: error writing header for %s, %s\n", filename,
527 strerror( errno ) );
528 goto error_return;
529 }
530
531 /* Write the object headers.
532 */
533 for( idx = 0; idx < num_objects; idx++ ) {
534 recs = fwrite_big32( lib->files[idx].m_time, fp );
535
536 /* Keep track of the offsets, and write 0 for now.
537 */
538 obj_offs[idx].filename_offset = ftell( fp );
539 recs += fwrite_big32( 0, fp );
540 obj_offs[idx].fullpath_offset = ftell( fp );
541 recs += fwrite_big32( 0, fp );
542 obj_offs[idx].data_offset = ftell( fp );
543 recs += fwrite_big32( 0, fp );
544
545 recs += fwrite_big32( lib->files[idx].object_size, fp );
546
547 if( recs != 5 ) {
548 fprintf( stderr, "ar: error writing object header for %s, %s\n",
549 filename, strerror( errno ) );
550 goto error_return;
551 }
552 }
553
554 /* Write the file names.
555 */
556 for( idx = 0; idx < num_objects; idx++ ) {
557 /* Need to make sure that all the file names we write into the
558 ** library DO NOT HAVE PATHS IN THEM, the world might end or something.
559 */
560 size_t name_len = 0;
561 char *name_ptr = strrchr( lib->names[idx], '/' );
562
563 if( name_ptr == NULL ) {
564 name_ptr = lib->names[idx];
565 } else {
566 name_ptr++;
567 }
568
569 name_len = strlen( name_ptr ) + 1;
570
571 obj_offs[idx].filename_offset_value = ftell( fp );
572 recs = fwrite( name_ptr, name_len, 1, fp );
573 obj_offs[idx].fullpath_offset_value = ftell( fp );
574 recs += fwrite( name_ptr, name_len, 1, fp );
575
576 if( recs != 2 ) {
577 fprintf( stderr, "ar: error writing object name for %s, %s\n",
578 filename, strerror( errno ) );
579 goto error_return;
580 }
581 }
582
583 /* Pad the file if necessary.
584 */
585 pad_amount = ftell( fp ) % 4;
586 if( pad_amount > 0 ) {
587 recs = fwrite( padding, pad_amount, 1, fp );
588
589 if( recs != 1 ) {
590 fprintf( stderr, "ar: error padding file %s, %s\n", filename,
591 strerror( errno ) );
592 goto error_return;
593 }
594 }
595
596 /* Write the object file data.
597 */
598 for( idx = 0; idx < num_objects; idx++ ) {
599 obj_offs[idx].data_offset_value = ftell( fp );
600
601 recs = fwrite( lib->data[idx], lib->files[idx].object_size, 1, fp );
602 if( recs != 1 ) {
603 fprintf( stderr, "ar: writing object data for %s, %s\n", filename,
604 strerror( errno ) );
605 goto error_return;
606 }
607
608 /* Add up the code/data size.
609 */
610 retval = add_object_sizes( (MWObject *)lib->data[idx],
611 &codesize, &datasize );
612 if( retval != B_OK ) {
613 fprintf( stderr, "ar - warning: %s is not an object file!\n",
614 lib->names[idx] );
615 goto error_return;
616 }
617
618 pad_amount = ftell( fp ) % 4;
619 if( pad_amount > 0 ) {
620 recs = fwrite( padding, pad_amount, 1, fp );
621
622 if( recs != 1 ) {
623 fprintf( stderr, "ar: error padding file %s, %s\n", filename,
624 strerror( errno ) );
625 goto error_return;
626 }
627 }
628 }
629
630 /* ----------------------------------------------------------------------
631 ** Write the offsets into the file. Pass 2.
632 */
633
634 /* Write the code/data sizes.
635 */
636 recs = fwrite_big32_seek( codesize, head_offs.codesize_offset, fp );
637 recs += fwrite_big32_seek( datasize, head_offs.datasize_offset, fp );
638 if( recs != 2 ) {
639 fprintf( stderr, "ar - error writing code and data sizes, %s\n",
640 strerror( errno ) );
641 goto error_return;
642 }
643
644 /* Write the offsets for each file.
645 */
646 for( idx = 0; idx < num_objects; idx++ ) {
647 recs = fwrite_big32_seek( obj_offs[idx].filename_offset_value,
648 obj_offs[idx].filename_offset, fp );
649 recs += fwrite_big32_seek( obj_offs[idx].fullpath_offset_value,
650 obj_offs[idx].fullpath_offset, fp );
651 recs += fwrite_big32_seek( obj_offs[idx].data_offset_value,
652 obj_offs[idx].data_offset, fp );
653
654 if( recs != 3 ) {
655 fprintf( stderr, "ar - error writing object offsets, %s\n",
656 strerror( errno ) );
657 goto error_return;
658 }
659 }
660
661 /* If all is OK, close the file and get out of here after nuking the
662 ** temp file (if any), preserving the original file mode, and preserving
663 ** the file attributes.
664 */
665
666 fclose( fp );
667
668 /* Preserve the original file mode bits.
669 */
670 retval = chmod( filename, mode_bits );
671 if( retval != 0 ) {
672 fprintf( stderr, "ar: unable to change file mode for %s, %s\n",
673 filename, strerror( errno ) );
674 }
675
676 /* Nuke the temp file (if any), after copying over any file attributes.
677 */
678 if( tmp_filename != NULL ) {
679 retval = copy_attrs( filename, tmp_filename );
680
681 retval = unlink( tmp_filename );
682 if( retval != 0 ) {
683 fprintf( stderr, "ar - error unlinking %s, %s\n", tmp_filename,
684 strerror( errno ) );
685 }
686 free( tmp_filename );
687 } else {
688 /* If there isn't a temp file, we should still give this new
689 ** file a file type attribute.
690 */
691 setfiletype( filename, "application/x-mw-library" );
692 }
693
694 return B_OK;
695
696error_return:
697 /* Restore the original file if we had any problems.
698 */
699 fclose( fp );
700
701 if( tmp_filename != NULL ) {
702 retval = unlink( filename );
703 retval = rename( tmp_filename, filename );
704 if( retval != 0 ) {
705 fprintf( stderr, "ar: can't restore %s to %s, %s\n",
706 tmp_filename, filename, strerror( errno ) );
707 }
708 }
709
710 return B_ERROR;
711}