This is qlipo.c in view mode; [Download] [Up]
/*
* QLIPO -- NeXT compatible, portable LIPO.
*
* You may not remove this comment header, though you may add to it.
* You may not remove attributions to the author or contributors, though
* you may add to them.
*
* Darcy Brockbank <samurai@hasc.ca>
* Hutchison Ave. Software
*/
#define PROGRAM_NAME "qlipo"
#define VERSION "Version 1.3."
/*
* Version 1.3
*
* - using stat() to verify identical files, rather than strcmp() (duh.)
* Apr 3, 1995
*
* Version 1.2
*
* - allowed file-over-file overwrite specification
* - caught signals for cleanup
*
* Version 1.0 <BETA>
* Dec 22, 1994
*
* This program is loosely based on plipo, by Christian Scheider, which
* served mainly as example code and inspiration that this could be done.
* The intent of this program is to furnish an upgrade path for people
* who have broken versions of 'lipo' as under NEXTSTEP 3.2 and previous
* versions. QLIPO is bug-for-bug compatible with lipo... well, not exactly
* bug-for-bug, but it does a good job of handling all the arguments you
* can pass it, while giving some more useful messages than our old
* buggy friend lipo.
*
* For the most part, this program is portable to any UNIX, though it works
* only on MACH-O files. At the moment, I'm being very conservative by
* keeping the byte alignments of the fat files at 8192 byte boundaries.
* While this will result in a bit of wasted space, it makes sure that
* all executables are happy. Future versions will allow for less
* conservative alignments.
*
* This program is being provided with the hope it will be interesting,
* and perhaps useful, but no guarantees are made as to it's functionality
* or usefulness, nor do I or Hutchison Ave. Software warrant it in any
* other way.
*
* Please keep me informed as to incompatabilities or problems, and I
* will do my best to fix things. Lots of this code is gross... I'm
* sorry about that, but I'm just trying to get it to work. Typical
* C programmer's excuse. I did write this over the course of 24 hours
* though, so please be gentle with me.
*
* TODO
*
* 1) support segalign
* 2) support multiple -remove flags... I had no idea this could be
* specified.
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "cpu.h"
#include <math.h>
#include <errno.h>
#include <sys/stat.h>
#ifdef NeXT
# include <libc.h>
#else
# include <unistd.h>
#endif
#include <signal.h>
#define FAT_MAGIC 0xcafebabe
#define NON_FAT_BIG_ENDIAN 0xfeedface
#define NON_FAT_LITTLE_ENDIAN 0xcefaedfe
#define STRDUP(a) (strcpy((char *)malloc(strlen((a))+1)),a)
/* sizeof() will pad the struct */
#define HEADER_BYTE_COUNT 8
typedef struct fat_header
{
char magic[4]; /* FAT_MAGIC */
char nfat_arch[4]; /* number of structs that follow */
} fat_header_t;
/* sizeof() will pad the struct */
#define ARCH_BYTE_COUNT 20
typedef struct fat_arch
{
char cputype[4]; /* cpu specifier (int) */
char cpusubtype[4]; /* machine specifier (int) */
char offset[4]; /* file offset to this object file */
char size[4]; /* size of this object file */
char align[4]; /* alignment as a power of 2 */
} fat_arch_t;
typedef struct non_fat_header
{
char magic[4]; /* FAT_MAGIC */
char cputype[4]; /* cpu specifier (int) */
} non_fat_header_t;
/* types of files */
#define MH_OBJECT 0x1
#define MH_EXECUTE 0x2
#define MH_FVMLIB 0x3
#define MH_CORE 0x4
#define MH_PRELOAD 0x5
#define MH_DYLIB 0x6
#define MH_DYLINKER 0x7
#define MH_BUNDLE 0x8
/* flags */
#define MH_NOUNDEFS 0x1
#define MH_INCRLINK 0x2
#define MH_DYLDLINK 0x4
#define LOADER_BYTE_COUNT 28
typedef struct loader
{
char magic[4]; /* magic number */
char cputype[4]; /* machine specifier (int) */
char cpusubtype[4]; /* machine specifier (int) */
char filetype[4]; /* the kind of file */
char ncmds[4]; /* size of all the load commands */
char sizeofcmds[4]; /* size of all the load commands */
char flags[4]; /* flags */
} loader_t;
#define GET32(x) ((unsigned long)((((unsigned char *)(x))[0] << 24) | \
(((unsigned char *)(x))[1] << 16) | \
(((unsigned char *)(x))[2] << 8) | \
(((unsigned char *)(x))[3])))
#define GET32L(x) ((unsigned long)((((unsigned char *)(x))[3] << 24) | \
(((unsigned char *)(x))[2] << 16) | \
(((unsigned char *)(x))[1] << 8) | \
(((unsigned char *)(x))[0])))
#define SET32(a, x) ((((unsigned char *)(a))[0] = (unsigned char )(x >> 24)), \
(((unsigned char *)(a))[1] = (unsigned char )(x >> 16)), \
(((unsigned char *)(a))[2] = (unsigned char )(x >> 8)), \
(((unsigned char *)(a))[3]) = (unsigned char )x)
#define COPY32(d, s) (((char *)(d))[0] = ((char *)(s))[0]), \
(((char *)(d))[1] = ((char *)(s))[1]), \
(((char *)(d))[2] = ((char *)(s))[2]), \
(((char *)(d))[3] = ((char *)(s))[3]);
#define IS_FAT(hdr) (GET32(((hdr)->magic)) == FAT_MAGIC)
#define IS_MAGIC(hdr) (GET32(((hdr)->magic)) == FAT_MAGIC || \
GET32(((hdr)->magic))==NON_FAT_BIG_ENDIAN || \
GET32(((hdr)->magic))==NON_FAT_LITTLE_ENDIAN)
/* 2^13 == 8192 */
#define DEFAULT_ALIGN 13
/*
* one of the operations we can perform...
*/
typedef enum {
_info=0,
_detailed_info,
_create,
_thin,
_replace,
_remove,
_extract,
_arch, /* non unique */
_output, /* non unique */
_segalign /* non unique ... and ignored under this version */
} operation_type_t;
typedef struct _operation_t {
operation_type_t op;
char * argv[3];
int argc;
} operation_t;
/*
* The switches we're looking for, and their matching arguments.
*/
typedef struct _arg_t {
const char * name;
int numargs;
operation_type_t op;
} arg_t;
/*
* Argument parsing aid .
*/
const arg_t argument_set[] = {
{"-info",0,_info},
{"-detailed_info",0,_detailed_info},
{"-arch",2,_arch},
{"-output",1,_output},
{"-o",1,_output},
{"-create",0,_create},
{"-thin",1,_thin},
{"-replace",2,_replace},
{"-remove",1,_remove},
{"-extract",1,_extract},
{"-segalign",2,_segalign},
{0}
};
/*
* Filename and association.
*/
typedef struct _filelist_t {
char * filename; /* our name */
int cputype; /* our cpu */
int cpusubtype; /* and subtype */
off_t size; /* our size */
long start; /* which fat arch we own in the list */
int isfat; /* are we fat? */
int skip; /* an arch to skip */
int suck; /* the only arch to keep */
int narch; /* the number of architectures */
unsigned long mode; /* the permissions */
} filelist_t;
/*
* A list of named input files. Null terminated, and counted for your
* convenience.
*/
filelist_t ** input_files=0;
int input_file_count=0;
/*
* An output file.
*/
filelist_t output_file={0,CPU_TYPE_ANY,0};
/*
* An overwrite file.
*/
static char * overwrite_file = 0;
/*
* A buffer for everyone to play in.
*/
#define BUFFER_SIZE 8192
static char buffer[BUFFER_SIZE];
#define DEBUG
#ifdef DEBUG
static int ArgC=0;
static char ** ArgV=0;
static void
quit(int flag)
{
if (flag){
int i;
fprintf(stderr,"ArgV: qlipo ");
for(i=1;i<ArgC;i++){
fprintf(stderr,"%s ",ArgV[i]);
}
fprintf(stderr,"\n");
}
if (overwrite_file) {
unlink(overwrite_file);
}
exit(flag);
}
#else
# define quit(flag) exit(flag)
#endif
static void
usage(void)
{
fprintf(stderr,
"qlipo: NeXT compatible lipo, by Darcy Brockbank <samurai@hasc.ca>\n"
" based on 'plipo' by Christian Schneider. " VERSION "\n"
"Note: ONLY one of -create, -thin <arch_type>, -extract <arch_type>,\n"
" -remove <arch_type>, -replace <arch_type> <file_name>, -info \n"
" or -detailed_info must be specified.\n"
"Usage: qlipo [input_file] ... [-arch <arch_type> input_file] ...\n"
" [-info] [-detailed_info] [-output output_file] [-create]\n"
" [-thin <arch_type>] [-extract <arch_type] ... [-remove <arch_type>]\n"
" ... [-replace <arch_type> <file_name>] ...\n");
quit(1);
}
static void
stat_file(int which)
{
const char * filename = input_files[which]->filename;
struct stat sb;
stat(filename,&sb);
input_files[which]->size = sb.st_size;
input_files[which]->mode = sb.st_mode;
}
/*
* Returns a structure representing the switch in the arglist, and what
* it is. If it's nothing (ie. a filename) then returns NULL.
*/
const arg_t *
switch_at(char *argv[],int i)
{
const arg_t *op;
for(op=argument_set;op->name;op++){
if (strcmp(op->name,argv[i])==0){
return op;
}
}
return 0;
}
/*
* Could expand this to check for syntax, but not right now
*/
int
switch_argc(const arg_t *op)
{
if (!op) return 0;
return op->numargs;
}
static void
grok_fileinfo(void)
{
FILE * file;
long i;
loader_t header;
if (!input_file_count){
fprintf(stderr,"%s: No input files specified.\n", PROGRAM_NAME);
usage();
}
for(i=0;i<input_file_count;i++){
file = fopen(input_files[i]->filename,"r");
if (!file){
fprintf(stderr,"%s: Can't open input file \"%s\", (%s)\n",
PROGRAM_NAME, input_files[i]->filename,strerror(errno));
quit(1);
}
if (fread(&header,1,LOADER_BYTE_COUNT,file)==LOADER_BYTE_COUNT){
switch(GET32(header.magic)){
case NON_FAT_BIG_ENDIAN:
input_files[i]->cputype=GET32(header.cputype);
input_files[i]->cpusubtype=GET32(header.cpusubtype);
input_files[i]->narch = 1;
break;
case NON_FAT_LITTLE_ENDIAN:
input_files[i]->cputype=GET32L(header.cputype);
input_files[i]->cpusubtype=GET32L(header.cpusubtype);
input_files[i]->narch = 1;
break;
case FAT_MAGIC:
input_files[i]->isfat=1;
input_files[i]->narch = GET32(((fat_header_t *)&header)->nfat_arch);
break;
default:
/* ignore it */
break;
}
}
stat_file(i);
fclose(file);
}
output_file.mode = input_files[0]->mode;
}
/*
* Find the operation. If we've already found it, then we should quit.
*/
static operation_t *
grok_operation(int argc, char *argv[])
{
int i;
const arg_t *found=0;
const arg_t *current=0;
int spot=0;
int numargs;
operation_t * perform = calloc(sizeof(operation_t),1);
for(i=1;i<argc;i++){
if ((current = switch_at(argv,i))){
switch (current->op){
case _segalign:
case _output:
case _arch:
/* ignore */
break;
default:
if (found){
usage();
} else {
found=current;
spot=i;
}
}
}
}
if (!found) usage();
perform->op = found->op;
numargs = switch_argc(found);
for(i=0;i<numargs;i++){
if (spot+i+1>=argc){
usage();
}
perform->argv[i]=argv[spot+i+1];
}
perform->argv[i]=0;
perform->argc=i;
return perform;
}
static void
add_file(char *filename, long type)
{
filelist_t * new = calloc(sizeof(filelist_t),1);
new->filename = filename;
new->cputype = type;
input_files[input_file_count++]=new;
}
/*
* Input filenames stand alone, or are preceded with -arch
*/
static void
grok_infiles(int argc, char *argv[])
{
int i;
const arg_t *arg;
/* we may need to add one under a -replace operation */
input_files = calloc(sizeof(filelist_t *),argc+1);
for(i=1;i<argc;i+=switch_argc(arg)+1){
arg = switch_at(argv,i);
if (!arg || arg->op == _arch){
if ((arg && i>=argc-2) || i>=argc) usage();
add_file(argv[(i)+((arg)?2:0)],cpu_type((arg)?argv[i+1]:0));
}
}
}
static void
grok_outfile(int argc, char *argv[])
{
int i;
for(i=1;i<argc;i++){
const arg_t *arg = switch_at(argv,i);
if (arg && arg->op == _output){
if (output_file.filename || i>=argc-1) usage();
output_file.filename=argv[i+1];
}
}
}
static void
grok_myargs(int argc, char *argv[])
{
int i;
for(i=1;i<argc;i++){
if (strcmp(argv[i],"-help")==0 ||
strcmp(argv[i],"--help")==0 ||
strcmp(argv[i],"-version")==0 ||
strcmp(argv[i],"--version")==0 ||
strcmp(argv[i],"-v")==0 ||
strcmp(argv[i],"-h")==0)
{
usage();
}
}
}
/*
* We want to find a unique operation, build an input filelist
* and figure out the output file. I could do it efficiently
* by building a hashtable, etc, but this is OK because the
* data set is so tiny.
*/
static operation_t *
grok_args(int argc, char *argv[])
{
operation_t *ret;
if (argc<=1) {
usage();
}
grok_myargs(argc,argv);
grok_infiles(argc,argv);
grok_outfile(argc,argv);
ret = grok_operation(argc,argv);
grok_fileinfo();
return ret;
}
static fat_arch_t *
fatgetarch(char *mem, int cpu)
{
fat_header_t *fh = (fat_header_t *)mem;
fat_arch_t *fa;
if (fh && (GET32(fh->magic) == FAT_MAGIC))
{
unsigned long num;
fa = (fat_arch_t *)(fh + 1);
for (num = GET32(fh->nfat_arch); num > 0; num--, fa++)
{
if ((cpu == CPU_TYPE_ANY) || (GET32(fa->cputype) == cpu))
return fa;
}
}
return (fat_arch_t *)0;
}
static unsigned long
fatgetoffset(char *mem, int cpu)
{
fat_arch_t *fa, *fa0;
if ((fa0 = fatgetarch(mem, CPU_TYPE_ANY)) && (fa = fatgetarch(mem, cpu)))
return GET32(fa->offset);
else
return 0;
}
static unsigned long
fatgetsize(char *mem, int cpu)
{
fat_arch_t *fa;
if ((fa = fatgetarch(mem, cpu)))
return GET32(fa->size);
else
return 0;
}
static void
fatsave(char *file, char *mem, int cpu)
{
FILE *out;
fat_arch_t *fa, *fa0;
if (file && ((out = fopen(file, "w")) != NULL))
{
if ((fa0 = fatgetarch(mem, CPU_TYPE_ANY)) && (fa = fatgetarch(mem, cpu)))
{
char num[4], msg[] = "\nExtracted with qlipo\n";
unsigned long t;
fwrite(mem, 4, 1, out);
SET32(num, 1);
fwrite(num, sizeof(num), 1, out);
t = GET32(fa->offset);
COPY32(fa->offset, fa0->offset);
fwrite(fa, sizeof(fat_arch_t), (size_t)1, out);
SET32(fa->offset, t);
fwrite(msg, sizeof(msg), 1, out);
fwrite(mem, (size_t)(GET32(fa0->offset) - sizeof(msg) -
sizeof(fat_arch_t) - 4 - 4), 1, out);
fwrite(mem + fatgetoffset(mem, cpu),
(size_t)fatgetsize(mem, cpu), 1, out);
}
fclose(out);
}
}
static char *
fatload(char *filename)
{
FILE *file = stdin;
char *mem = NULL;
long old, size = 16384;
if (filename && (!strcmp(filename, "-") || (file = fopen(filename, "r"))))
{
for (mem = (char *)malloc((size_t)size), old = 0;
mem = (char *)realloc(mem, (size_t)size);
old += (size - old), size *= 2)
{
if (fread(mem + old, 1, (size_t)(size - old), file) < (size - old))
{
if (ferror(file))
{
free(mem);
mem = NULL;
}
break;
}
}
fclose(file);
}
return mem;
}
static long
type_from_header(non_fat_header_t *nfh)
{
long ct = CPU_TYPE_ANY;
if (GET32(nfh->magic) == NON_FAT_BIG_ENDIAN)
{
ct = (int)GET32(nfh->cputype);
} else if (GET32(nfh->magic) == NON_FAT_LITTLE_ENDIAN)
{
ct = (int)GET32L(nfh->cputype);
}
return ct;
}
static long
type_from_fat_arch(fat_arch_t *fa)
{
return GET32(fa->cputype);
}
static long
byte_alignment(fat_arch_t *fa)
{
return pow(2,GET32(fa->align));
}
static void
fatanalyse(char *mem, const char *filename)
{
fat_header_t *fh = (fat_header_t *)mem;
non_fat_header_t *nfh = (non_fat_header_t *)mem;
int ct = -1;
if (fh)
{
if (GET32(fh->magic) == FAT_MAGIC)
{
long num = GET32(fh->nfat_arch);
fat_arch_t *fa = (fat_arch_t *)(fh + 1);
printf("Architectures (%ld) in the fat file: \"%s\" are:", num, filename);
for ( ; num > 0; fa++, num--)
{
int ct = (int)GET32(fa->cputype);
if ((ct < -1) || (ct >= (sizeof(cpus) / sizeof(char *))))
ct = 1;
printf(" %s", cpus[ct + 1]);
}
printf("\n");
} else {
ct = type_from_header(nfh);
if (ct >= 0) {
printf("Non-fat file: \"%s\" is architecture (%d)", filename,ct);
if ((ct < -1) || (ct >= (sizeof(cpus) / sizeof(char *))))
ct = 1;
printf(": %s\n", cpus[ct + 1]);
} else {
printf("unknown file type (magic: %08x)\n", (unsigned)GET32(fh->magic));
}
}
}
}
static void
info(void)
{
char * mem;
int i;
for(i=0;input_files[i];i++){
if ((mem = fatload(input_files[i]->filename)))
{
fatanalyse(mem,input_files[i]->filename);
free(mem);
}
}
}
static void
fatprint_header(fat_header_t *header, const char *filename)
{
if (IS_FAT(header)){
printf("%s: Fat header in: \"%s\"\n"
"fat_magic 0x%lx\n"
"nfat_arch %lx\n",
PROGRAM_NAME,
filename,
GET32(header->magic),
GET32(header->nfat_arch));
} else {
printf("Input file \"%s\" is not a fat file.\n",filename);
}
}
static void
fatprint_arch(fat_arch_t *arch)
{
long type = GET32(arch->cputype);
long subtype = GET32(arch->cpusubtype);
const char * architecture = arch_from_type(type);
const char * cputype = get_cputype(type);
const char * cpusubtype = get_cpusubtype(type,subtype);
long offset = GET32(arch->offset);
long size = GET32(arch->size);
long align = GET32(arch->align);
printf("architecture %s\n"
" cputype %s\n"
" cpusubtype %s\n"
" offset %ld\n"
" size %ld\n"
" align 2^%ld (%.0f)\n",
architecture,cputype,cpusubtype,offset,size,align,pow(2.0,(float)align));
}
static void
fatprint(const char *filename)
{
FILE *file = fopen(filename,"r");
fat_header_t header;
fat_arch_t arch;
if (!file) {
fprintf(stderr,"%s: Can't open \"%s\", (%s)\n",
PROGRAM_NAME,
filename,strerror(errno));
quit(1);
}
fread(&header,1,HEADER_BYTE_COUNT,file);
fatprint_header(&header,filename);
if (IS_FAT(&header)){
long i,count=GET32(header.nfat_arch);
for(i=0;i<count;i++){
fread(&arch,1,ARCH_BYTE_COUNT,file);
fatprint_arch(&arch);
}
} else {
/* nfat_arch is filled with the cpu type if the file is not fat */
long type = type_from_header((non_fat_header_t *)&header);
printf("Non-fat file: \"%s\" is architecture: %s\n",
filename,
arch_from_type(type));
}
fclose(file);
}
static void
detailed_info(void)
{
int i;
for(i=0;input_files[i];i++){
fatprint(input_files[i]->filename);
}
}
static void
write_fat_header(FILE *file, long archs)
{
fat_header_t header;
SET32(header.magic,FAT_MAGIC);
SET32(header.nfat_arch,archs);
if (fwrite(&header,1,HEADER_BYTE_COUNT,file)!=HEADER_BYTE_COUNT){
fprintf(stderr,"Error writing to file... (%s)\n",strerror(errno));
quit(1);
}
}
static void
set_fat_arch(fat_arch_t *a,long ct, long cst, long o, long s, long algn)
{
SET32(a->cputype,ct);
SET32(a->cpusubtype,cst);
SET32(a->offset,o);
SET32(a->size,s);
SET32(a->align,algn);
}
static fat_arch_t *
make_fat_arch_at(long which)
{
long size = input_files[which]->size;
long offset = 0; /* we consider it pure data... */
long cputype = input_files[which]->cputype;
long cpusubtype = input_files[which]->cpusubtype;
long align = DEFAULT_ALIGN;
fat_arch_t * a;
if (cputype == CPU_TYPE_ANY){
fprintf(stderr,"%s: Can't figure out the arch type for \"%s\".\n",
PROGRAM_NAME,input_files[which]->filename);
usage();
}
a = calloc(sizeof(fat_arch_t),1);
set_fat_arch(a,cputype,cpusubtype,offset,size,align);
return a;
}
static long
add_arch(fat_arch_t ***fa, fat_arch_t *add, long at)
{
if (!*fa) {
*fa = calloc(sizeof(fat_arch_t *),1);
} else {
*fa = realloc(*fa, sizeof(fat_arch_t *)*(at+1));
}
(*fa)[at]=add;
return 1;
}
static long
load_fat_arch(long which, fat_arch_t ***fa, long total)
{
FILE * file = fopen(input_files[which]->filename,"r");
fat_header_t header;
fat_arch_t *a;
if (!file) { /* Can't assume it's still here. */
fprintf(stderr,"%s: Can't open \"%s\", (%s)\n",
PROGRAM_NAME,
input_files[which]->filename,strerror(errno));
quit(1);
}
fread(&header,1,HEADER_BYTE_COUNT,file);
if (input_files[which]->isfat){
long i,count=GET32(header.nfat_arch);
for(i=0;i<count;i++){
a = calloc(sizeof(fat_arch_t),1);
fread(a,1,ARCH_BYTE_COUNT,file);
total += add_arch(fa,a,total);
}
} else {
a = make_fat_arch_at(which);
total += add_arch(fa,a,total);
}
fclose(file);
return total;
}
static fat_arch_t **
load_fat_archs(long *total)
{
long i;
long count = 0;
fat_arch_t **fa = 0;
for(i=0;i<input_file_count;i++){
input_files[i]->start = count;
count = load_fat_arch(i,&fa,count);
}
*total = count;
return fa;
}
static long
file_owning_fa(long chunk)
{
int i;
for(i=input_file_count-1;i>0;i--){
if (input_files[i]->start <= chunk){
return i;
}
}
return 0;
}
static void
consistency_check(fat_arch_t **fa, long count)
{
long archs[CPU_TYPE_MAX];
long i;
for(i=0;i<CPU_TYPE_MAX;i++) archs[i]=-1;
for(i=0;i<count;i++){
long current = type_from_fat_arch(fa[i]);
long which, suck, skip;
if (current<0 || current>=CPU_TYPE_MAX){
fprintf(stderr,"%s: File \"%s\" has unknown architecture.\n",
PROGRAM_NAME,input_files[file_owning_fa(i)]->filename);
quit(1);
}
which = file_owning_fa(i);
suck = input_files[which]->suck;
skip = input_files[which]->skip;
/*
* See if this file is losing this architecture, or if it is the
* only one being preserved.
*/
if (skip != current && (!suck || suck==current)){
if (archs[current]!=-1) {
fprintf(stderr,
"%s: \"%s\" and \"%s\" share common architecture (%s)\n"
" and can't exist in the same fat output file.\n",
PROGRAM_NAME,
input_files[archs[current]]->filename,
input_files[file_owning_fa(i)]->filename,
get_cputype(current));
quit(1);
}
archs[current] = which;
}
}
}
static int
files_are_identical(const char *f1, const char *f2)
{
if (strcmp(f1,f2)==0){
return 1;
} else {
struct stat sb1, sb2;
if (stat(f1,&sb1)){
fprintf(stderr,"%s: Error statting \"%s\", (%s)\n",
PROGRAM_NAME, f1,strerror(errno));
quit(1);
}
if (stat(f2,&sb2)){
fprintf(stderr,"%s: Error statting \"%s\", (%s)\n",
PROGRAM_NAME, f1,strerror(errno));
quit(1);
}
return (sb1.st_ino == sb2.st_ino);
}
}
static FILE *
muddle_outfile(void)
{
int i;
FILE * file = 0;
for(i=0;i<input_file_count;i++){
if (files_are_identical(output_file.filename,input_files[i]->filename)){
overwrite_file = malloc(MAXPATHLEN);
sprintf(overwrite_file,"/tmp/%s.%d",PROGRAM_NAME,getpid());
file = (fopen(overwrite_file,"w"));
}
}
if (!file) {
file = fopen(output_file.filename,"w");
}
if (!(file)){
fprintf(stderr,"%s: Error opening \"%s\", (%s)\n",
PROGRAM_NAME, overwrite_file ? overwrite_file :
output_file.filename,strerror(errno));
quit(1);
}
return file;
}
static void
copy_filename(const char *dest, const char *source)
{
char tmp[strlen(dest)+strlen(source)+128];
sprintf(tmp,"cp %s %s",source,dest);
system(tmp);
}
static void
close_output(FILE *file)
{
FILE * source, *output;
fclose(file);
if (overwrite_file){
output = fopen(output_file.filename,"w");
if (!(output)){
fprintf(stderr,"%s: Error opening \"%s\", (%s)\n",
PROGRAM_NAME, output_file.filename,strerror(errno));
quit(1);
}
source = fopen(overwrite_file,"r");
if (!(source)){
fprintf(stderr,"%s: Error opening \"%s\", (%s)\n",
PROGRAM_NAME, overwrite_file,strerror(errno));
quit(1);
}
fclose(source);
fclose(output);
copy_filename(output_file.filename,overwrite_file);
}
chmod(output_file.filename,output_file.mode);
}
static FILE *
grok_output(void)
{
FILE * file;
if (!output_file.filename)
{
fprintf(stderr,"%s: No output file specified.\n",PROGRAM_NAME);
usage();
}
file = muddle_outfile();
return file;
}
static void
write_fat_archlist(FILE *output, fat_arch_t **fa, long total)
{
long offset=0;
long align,j,i,stop;
long old_offset;
long skip;
long suck;
for(i=0;i<input_file_count;i++){
if (i<input_file_count-1){
stop = input_files[i+1]->start;
} else {
stop = total;
}
skip = input_files[i]->skip;
suck = input_files[i]->suck;
for(j=input_files[i]->start;j<stop;j++){
long current = GET32(fa[j]->cputype);
if (current != skip){
if (current == suck || !suck){
align = byte_alignment(fa[j]);
offset = ((offset / align) + 1) * align;
old_offset = GET32(fa[j]->offset);
SET32(fa[j]->offset,offset);
fwrite(fa[j],1,ARCH_BYTE_COUNT,output);
offset+=GET32(fa[j]->size);
/* we set it back so we know were to get it later */
SET32(fa[j]->offset,old_offset);
}
}
}
}
}
static void
write_pad(FILE *output, long start, long align)
{
long count = (align) ? align - start % align : 0;
long i;
char null='\0';
for(i=0;i<count;i++){
fwrite(&null,1,1,output);
}
}
static void
write_binary(FILE *output, long align, long which, long where, long size)
{
long tell = ftell(output);
/* assume it's still ok */
FILE * source = fopen(input_files[which]->filename,"r");
long bufsiz;
long current;
write_pad(output,tell,align);
fseek(source,where,SEEK_SET);
bufsiz = (BUFFER_SIZE > input_files[which]->size) ?
input_files[which]->size : BUFFER_SIZE;
for(current=bufsiz;(size>0);){
if (fread(buffer,1,current,source)!=current){
fprintf(stderr,"%s: Error reading from \"%s\", (%s)\n",
PROGRAM_NAME,input_files[which]->filename,strerror(errno));
}
if (fwrite(buffer,1,current,output)!=current){
fprintf(stderr,"%s: Error writing to \"%s\", (%s)\n",
PROGRAM_NAME,output_file.filename,strerror(errno));
}
size -= current;
if (size<bufsiz){
current = size;
}
}
fclose(source);
}
static void
write_fat_binaries(FILE *output, fat_arch_t **fa, long total)
{
long offset=0;
long align,j,i,stop;
long where,size;
long skip, suck;
for(i=0;i<input_file_count;i++){
if (i<input_file_count-1){
stop = input_files[i+1]->start;
} else {
stop = total;
}
skip = input_files[i]->skip;
suck = input_files[i]->suck;
for(j=input_files[i]->start;j<stop;j++){
long current = GET32(fa[j]->cputype);
if (current != skip){
if (current == suck || !suck){
align = byte_alignment(fa[j]);
offset = ((offset / align) + 1) * align;
where = GET32(fa[j]->offset);
size = GET32(fa[j]->size);
write_binary(output,align,i,where,size);
offset+=size;
}
}
}
}
}
/* take the infiles, and meld them into the outfile */
static void
create(const operation_t *perform)
{
FILE * output;
long total;
fat_arch_t **fa;
fa = load_fat_archs(&total);
consistency_check(fa,total);
output = grok_output();
write_fat_header(output,total);
write_fat_archlist(output,fa,total);
write_fat_binaries(output,fa,total);
close_output(output);
}
static fat_arch_t *
find_fat_arch(fat_arch_t **fa, long count, int type)
{
long i;
for(i=0;i<count;i++){
if (GET32(fa[i]->cputype)==type){
return fa[i];
}
}
return 0;
}
static void
assert_one_input(const char *tag)
{
if (input_file_count!=1){
fprintf(stderr,"%s: %s requires that you specify a single "
"input file.\n", PROGRAM_NAME, tag);
usage();
}
}
static void
assert_arch_arg(const operation_t *perform, const char *tag)
{
if (perform->argc<1 || cpu_type(perform->argv[0])<=CPU_TYPE_ANY){
fprintf(stderr, "%s: %s requires an architecture argument, "
"like \"m68k\"\n", PROGRAM_NAME,tag);
usage();
}
}
static void
assert_fat(int which, const char *tag)
{
if (!input_files[which]->isfat){
fprintf(stderr,"%s: \"%s\" is not fat, and %s only operates "
"on fat files.\n",PROGRAM_NAME,input_files[which]->filename,tag);
quit(1);
}
}
static void
assert_arch(fat_arch_t *single, long type, const char *tag)
{
if (!single){
fprintf(stderr,
"%s: \"%s\" doesn't contain the architecture \"%s\" passed "
"to %s.\n",
PROGRAM_NAME,input_files[0]->filename,get_cputype(type),tag);
quit(1);
}
}
/*
* Take the infiles, and meld them into a fat outfile with one arch
*/
static void
extract(const operation_t *perform)
{
int type;
FILE * output;
long total;
fat_arch_t **fa;
fat_arch_t *single;
assert_arch_arg(perform,"-extract");
type = cpu_type(perform->argv[0]);
assert_one_input("-extract");
fa = load_fat_archs(&total);
assert_fat(0,"-extract");
consistency_check(fa,total);
single = find_fat_arch(fa,total,type);
assert_arch(single,type,"-extract");
output = grok_output();
write_fat_header(output,1);
write_fat_archlist(output,&single,1);
write_fat_binaries(output,&single,1);
close_output(output);
}
/*
* Take the infile, and meld it into a thin outfile
*/
static void
thin(const operation_t *perform)
{
FILE * output;
long total;
fat_arch_t **fa, *single;
int type;
assert_arch_arg(perform,"-thin");
type = cpu_type(perform->argv[0]);
assert_one_input("-thin");
fa = load_fat_archs(&total);
assert_fat(0,"-thin");
consistency_check(fa,total);
single = find_fat_arch(fa,total,type);
assert_arch(single,type,"-thin");
output = grok_output();
write_binary(output,0,0,GET32(single->offset),GET32(single->size));
close_output(output);
}
static void
assert_file_arg(const operation_t *perform, int index, const char *tag)
{
if (perform->argc<=index){
fprintf(stderr,"%s: %s requires more arguments than was given (%d)\n",
PROGRAM_NAME,tag,index);
usage();
}
}
/*
* Take the infile, and replace the named arch with the named arch
* from the argument file.
*/
static void
replace(const operation_t *perform)
{
FILE * output;
long total;
fat_arch_t **fa, *single;
int type;
assert_one_input("-replace");
assert_fat(0,"-replace");
assert_arch_arg(perform,"-replace");
type = cpu_type(perform->argv[0]);
assert_file_arg(perform,1,"-replace");
add_file(perform->argv[1],CPU_TYPE_ANY);
/*
* Rerun this to assert the new file we just added.
*/
grok_fileinfo();
/*
* file 0 is the source file, file 1 is the suck file
*/
input_files[0]->skip = type;
input_files[1]->suck = type;
fa = load_fat_archs(&total);
consistency_check(fa,total);
single = find_fat_arch(fa,total,type);
/* this is wrong... we have to check both files */
assert_arch(single,type,"-replace");
output = grok_output();
write_fat_header(output,input_files[0]->narch);
write_fat_archlist(output,fa,total);
write_fat_binaries(output,fa,total);
close_output(output);
}
static void
assert_total(int archcount, const char *tag)
{
if (!archcount){
fprintf(stderr, "%s: %s operation would result in an empty fat file\n",
PROGRAM_NAME,tag);
quit(1);
}
}
/*
* Remove the named architecture.
*/
static void
remove_arch(const operation_t *perform)
{
FILE * output;
long total;
fat_arch_t **fa, *single;
int type;
assert_arch_arg(perform,"-remove");
type = cpu_type(perform->argv[0]);
assert_one_input("-remove");
fa = load_fat_archs(&total);
assert_fat(0,"-remove");
consistency_check(fa,total);
single = find_fat_arch(fa,total,type);
assert_arch(single,type,"-remove");
output = grok_output();
assert_total(total-1,"-remove");
write_fat_header(output,total-1);
input_files[0]->skip = type;
write_fat_archlist(output,fa,total);
write_fat_binaries(output,fa,total);
close_output(output);
}
void
handler(int sig)
{
const char *die=0;
switch(sig)
{
case SIGINT:
die = PROGRAM_NAME ": Caught an INTERRUPT. Bye.\n";
break;
case SIGHUP:
die = PROGRAM_NAME ": Caught a HANGUP. That's rude! Bye.\n";
break;
case SIGTERM:
die = PROGRAM_NAME ": Caught a SIGTERM. Bye.\n";
break;
}
if (die){
fprintf(stderr,"%s",die);
quit(1);
}
}
static void
setup_signals(void)
{
signal(SIGINT,handler);
signal(SIGHUP,handler);
signal(SIGTERM,handler);
}
int
main(int argc, char **argv)
{
int i, xcpu = -1;
char *mem;
extern int optind;
operation_t * perform;
#ifdef DEBUG
ArgC = argc;
ArgV = argv;
#endif
setup_signals();
perform = grok_args(argc,argv);
switch(perform->op){
case _info:
info();
break;
case _detailed_info:
detailed_info();
break;
case _create:
create(perform);
break;
case _extract:
extract(perform);
break;
case _thin:
thin(perform);
break;
case _replace:
replace(perform);
break;
case _remove:
remove_arch(perform);
break;
default: /* arch, output, segalign */
usage();
break;
}
quit(0);
if (xcpu > 0)
printf("Extracting cpu type '%s'\n", cpus[xcpu + 1]);
if (optind == argc)
{
}
for (i = optind; i < argc; i++)
{
if ((mem = fatload(argv[i])))
{
char buf[1024];
printf("File '%s': ", argv[i]);
fatanalyse(mem,0);
if (xcpu > 0)
{
if (fatgetarch(mem, xcpu))
{
sprintf(buf, "%s.%s", argv[i], cpus[xcpu + 1]);
fatsave(buf, mem, xcpu);
}
else
printf("specified architecture not found.\n");
}
free(mem);
}
}
return 0;
}
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.