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.