| // Inferno utils/iar/ar.c | |
| // http://code.google.com/p/inferno-os/source/browse/utils/iar/ar.c | |
| // | |
| // Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. | |
| // Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) | |
| // Portions Copyright © 1997-1999 Vita Nuova Limited | |
| // Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) | |
| // Portions Copyright © 2004,2006 Bruce Ellis | |
| // Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) | |
| // Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others | |
| // Portions Copyright © 2009 The Go Authors. All rights reserved. | |
| // | |
| // Permission is hereby granted, free of charge, to any person obtaining a copy | |
| // of this software and associated documentation files (the "Software"), to deal | |
| // in the Software without restriction, including without limitation the rights | |
| // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| // copies of the Software, and to permit persons to whom the Software is | |
| // furnished to do so, subject to the following conditions: | |
| // | |
| // The above copyright notice and this permission notice shall be included in | |
| // all copies or substantial portions of the Software. | |
| // | |
| // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| // THE SOFTWARE. | |
| /* | |
| * ar - portable (ascii) format version | |
| */ | |
| /* protect a couple of our names */ | |
| #define select your_select | |
| #define rcmd your_rcmd | |
| #include <u.h> | |
| #include <time.h> | |
| #include <libc.h> | |
| #include <bio.h> | |
| #include <mach.h> | |
| #include "../../libmach/obj.h" | |
| #include <ar.h> | |
| #undef select | |
| #undef rcmd | |
| /* | |
| * The algorithm uses up to 3 temp files. The "pivot member" is the | |
| * archive member specified by and a, b, or i option. The temp files are | |
| * astart - contains existing members up to and including the pivot member. | |
| * amiddle - contains new files moved or inserted behind the pivot. | |
| * aend - contains the existing members that follow the pivot member. | |
| * When all members have been processed, function 'install' streams the | |
| * temp files, in order, back into the archive. | |
| */ | |
| typedef struct Arsymref | |
| { | |
| char *name; | |
| char *file; | |
| int type; | |
| int len; | |
| vlong offset; | |
| struct Arsymref *next; | |
| } Arsymref; | |
| typedef struct Armember /* Temp file entry - one per archive member */ | |
| { | |
| struct Armember *next; | |
| struct ar_hdr hdr; | |
| long size; | |
| long date; | |
| void *member; | |
| } Armember; | |
| typedef struct Arfile /* Temp file control block - one per tempfile */ | |
| { | |
| int paged; /* set when some data paged to disk */ | |
| char *fname; /* paging file name */ | |
| int fd; /* paging file descriptor */ | |
| vlong size; | |
| Armember *head; /* head of member chain */ | |
| Armember *tail; /* tail of member chain */ | |
| Arsymref *sym; /* head of defined symbol chain */ | |
| } Arfile; | |
| typedef struct Hashchain | |
| { | |
| char *name; | |
| char *file; | |
| struct Hashchain *next; | |
| } Hashchain; | |
| #define NHASH 1024 | |
| /* | |
| * macro to portably read/write archive header. | |
| * 'cmd' is read/write/Bread/Bwrite, etc. | |
| */ | |
| #define HEADER_IO(cmd, f, h) cmd(f, h.name, sizeof(h.name)) != sizeof(h.name)\ | |
| || cmd(f, h.date, sizeof(h.date)) != sizeof(h.date)\ | |
| || cmd(f, h.uid, sizeof(h.uid)) != sizeof(h.uid)\ | |
| || cmd(f, h.gid, sizeof(h.gid)) != sizeof(h.gid)\ | |
| || cmd(f, h.mode, sizeof(h.mode)) != sizeof(h.mode)\ | |
| || cmd(f, h.size, sizeof(h.size)) != sizeof(h.size)\ | |
| || cmd(f, h.fmag, sizeof(h.fmag)) != sizeof(h.fmag) | |
| /* constants and flags */ | |
| char *man = "mrxtdpq"; | |
| char *opt = "uvnbailogS"; | |
| char artemp[] = "/tmp/vXXXXX"; | |
| char movtemp[] = "/tmp/v1XXXXX"; | |
| char tailtemp[] = "/tmp/v2XXXXX"; | |
| char symdef[] = "__.SYMDEF"; | |
| char pkgdef[] = "__.PKGDEF"; | |
| int aflag; /* command line flags */ | |
| int bflag; | |
| int cflag; | |
| int gflag; | |
| int oflag; | |
| int uflag; | |
| int vflag; | |
| int Pflag; /* remove leading file prefix */ | |
| int Sflag; /* force mark Go package as safe */ | |
| int errors; | |
| Arfile *astart, *amiddle, *aend; /* Temp file control block pointers */ | |
| int allobj = 1; /* set when all members are object files of the same type */ | |
| int symdefsize; /* size of symdef file */ | |
| char *pkgstmt; /* string "package foo" */ | |
| char *objhdr; /* string "go object darwin 386 release.2010-01-01 2345+" */ | |
| int dupfound; /* flag for duplicate symbol */ | |
| Hashchain *hash[NHASH]; /* hash table of text symbols */ | |
| #define ARNAMESIZE sizeof(astart->tail->hdr.name) | |
| char poname[ARNAMESIZE+1]; /* name of pivot member */ | |
| char *file; /* current file or member being worked on */ | |
| Biobuf bout; | |
| Biobuf bar; | |
| char *prefix; | |
| int pkgdefsafe; /* was __.PKGDEF marked safe? */ | |
| void arcopy(Biobuf*, Arfile*, Armember*); | |
| int arcreate(char*); | |
| void arfree(Arfile*); | |
| void arinsert(Arfile*, Armember*); | |
| void *armalloc(int); | |
| char *arstrdup(char*); | |
| void armove(Biobuf*, Arfile*, Armember*); | |
| void arread(Biobuf*, Armember*); | |
| void arstream(int, Arfile*); | |
| int arwrite(int, Armember*); | |
| int bamatch(char*, char*); | |
| int duplicate(char*, char**); | |
| Armember *getdir(Biobuf*); | |
| void getpkgdef(char**, int*); | |
| int getspace(void); | |
| void install(char*, Arfile*, Arfile*, Arfile*, int); | |
| void loadpkgdata(char*, int); | |
| void longt(Armember*); | |
| int match(int, char**); | |
| void mesg(int, char*); | |
| Arfile *newtempfile(char*); | |
| Armember *newmember(void); | |
| void objsym(Sym*, void*); | |
| int openar(char*, int, int); | |
| int page(Arfile*); | |
| void pmode(long); | |
| void rl(int); | |
| void scanobj(Biobuf*, Arfile*, long); | |
| void scanpkg(Biobuf*, long); | |
| void select(int*, long); | |
| void setcom(void(*)(char*, int, char**)); | |
| void skip(Biobuf*, vlong); | |
| void checksafe(Biobuf*, vlong); | |
| int symcomp(void*, void*); | |
| void trim(char*, char*, int); | |
| void usage(void); | |
| void wrerr(void); | |
| void wrsym(Biobuf*, long, Arsymref*); | |
| int arread_cutprefix(Biobuf*, Armember*); | |
| void rcmd(char*, int, char**); /* command processing */ | |
| void dcmd(char*, int, char**); | |
| void xcmd(char*, int, char**); | |
| void tcmd(char*, int, char**); | |
| void pcmd(char*, int, char**); | |
| void mcmd(char*, int, char**); | |
| void qcmd(char*, int, char**); | |
| void (*comfun)(char*, int, char**); | |
| void | |
| main(int argc, char *argv[]) | |
| { | |
| char *cp; | |
| Binit(&bout, 1, OWRITE); | |
| if(argc < 3) | |
| usage(); | |
| for (cp = argv[1]; *cp; cp++) { | |
| switch(*cp) { | |
| case 'a': aflag = 1; break; | |
| case 'b': bflag = 1; break; | |
| case 'c': cflag = 1; break; | |
| case 'd': setcom(dcmd); break; | |
| case 'g': gflag = 1; break; | |
| case 'i': bflag = 1; break; | |
| case 'l': | |
| strcpy(artemp, "vXXXXX"); | |
| strcpy(movtemp, "v1XXXXX"); | |
| strcpy(tailtemp, "v2XXXXX"); | |
| break; | |
| case 'm': setcom(mcmd); break; | |
| case 'o': oflag = 1; break; | |
| case 'p': setcom(pcmd); break; | |
| case 'q': setcom(qcmd); break; | |
| case 'r': setcom(rcmd); break; | |
| case 't': setcom(tcmd); break; | |
| case 'u': uflag = 1; break; | |
| case 'v': vflag = 1; break; | |
| case 'x': setcom(xcmd); break; | |
| case 'S': Sflag = 1; break; | |
| case 'P': Pflag = 1; break; | |
| default: | |
| fprint(2, "gopack: bad option `%c'\n", *cp); | |
| exits("error"); | |
| } | |
| } | |
| if (aflag && bflag) { | |
| fprint(2, "gopack: only one of 'a' and 'b' can be specified\n"); | |
| usage(); | |
| } | |
| if(aflag || bflag) { | |
| trim(argv[2], poname, sizeof(poname)); | |
| argv++; | |
| argc--; | |
| if(argc < 3) | |
| usage(); | |
| } | |
| if(Pflag) { | |
| if(argc < 4) { | |
| fprint(2, "gopack: P flag requires prefix argument\n"); | |
| usage(); | |
| } | |
| prefix = argv[2]; | |
| argv++; | |
| argc--; | |
| } | |
| if(comfun == 0) { | |
| if(uflag == 0) { | |
| fprint(2, "gopack: one of [%s] must be specified\n", man); | |
| usage(); | |
| } | |
| setcom(rcmd); | |
| } | |
| cp = argv[2]; | |
| argc -= 3; | |
| argv += 3; | |
| (*comfun)(cp, argc, argv); /* do the command */ | |
| if(errors && cflag) | |
| remove(cp); | |
| cp = 0; | |
| while (argc--) { | |
| if (*argv) { | |
| fprint(2, "gopack: %s not found\n", *argv); | |
| cp = "error"; | |
| } | |
| argv++; | |
| } | |
| if (errors) | |
| cp = "error"; | |
| exits(cp); | |
| } | |
| /* | |
| * select a command | |
| */ | |
| void | |
| setcom(void (*fun)(char *, int, char**)) | |
| { | |
| if(comfun != 0) { | |
| fprint(2, "gopack: only one of [%s] allowed\n", man); | |
| usage(); | |
| } | |
| comfun = fun; | |
| } | |
| /* | |
| * perform the 'r' and 'u' commands | |
| */ | |
| void | |
| rcmd(char *arname, int count, char **files) | |
| { | |
| int fd; | |
| int i; | |
| Arfile *ap; | |
| Armember *bp; | |
| Dir *d; | |
| Biobuf *bfile; | |
| fd = openar(arname, ORDWR, 1); | |
| if (fd >= 0) { | |
| Binit(&bar, fd, OREAD); | |
| Bseek(&bar,seek(fd,0,1), 1); | |
| } | |
| astart = newtempfile(artemp); | |
| ap = astart; | |
| aend = 0; | |
| for(i = 0; fd >= 0; i++) { | |
| bp = getdir(&bar); | |
| if (!bp) | |
| break; | |
| if (bamatch(file, poname)) { /* check for pivot */ | |
| aend = newtempfile(tailtemp); | |
| ap = aend; | |
| } | |
| /* pitch symdef file */ | |
| if (i == 0 && strcmp(file, symdef) == 0) { | |
| skip(&bar, bp->size); | |
| continue; | |
| } | |
| /* pitch pkgdef file but remember whether it was marked safe */ | |
| if (gflag && strcmp(file, pkgdef) == 0) { | |
| checksafe(&bar, bp->size); | |
| continue; | |
| } | |
| /* | |
| * the plan 9 ar treats count == 0 as equivalent | |
| * to listing all the archive's files on the command line: | |
| * it will try to open every file name in the archive | |
| * and copy that file into the archive if it exists. | |
| * for go we disable that behavior, because we use | |
| * r with no files to make changes to the archive itself, | |
| * using the S or P flags. | |
| */ | |
| if (!match(count, files)) { | |
| scanobj(&bar, ap, bp->size); | |
| arcopy(&bar, ap, bp); | |
| continue; | |
| } | |
| bfile = Bopen(file, OREAD); | |
| if (!bfile) { | |
| if (count != 0) { | |
| fprint(2, "gopack: cannot open %s\n", file); | |
| errors++; | |
| } | |
| scanobj(&bar, ap, bp->size); | |
| arcopy(&bar, ap, bp); | |
| continue; | |
| } | |
| d = dirfstat(Bfildes(bfile)); | |
| if(d == nil) | |
| fprint(2, "gopack: cannot stat %s: %r\n", file); | |
| if (uflag && (d==nil || d->mtime <= bp->date)) { | |
| scanobj(&bar, ap, bp->size); | |
| arcopy(&bar, ap, bp); | |
| Bterm(bfile); | |
| free(d); | |
| continue; | |
| } | |
| mesg('r', file); | |
| skip(&bar, bp->size); | |
| scanobj(bfile, ap, d->length); | |
| free(d); | |
| armove(bfile, ap, bp); | |
| Bterm(bfile); | |
| } | |
| if(fd >= 0) | |
| close(fd); | |
| /* copy in remaining files named on command line */ | |
| for (i = 0; i < count; i++) { | |
| file = files[i]; | |
| if(file == 0) | |
| continue; | |
| files[i] = 0; | |
| bfile = Bopen(file, OREAD); | |
| if (!bfile) { | |
| fprint(2, "gopack: cannot open %s\n", file); | |
| errors++; | |
| } else { | |
| mesg('a', file); | |
| d = dirfstat(Bfildes(bfile)); | |
| if (d == nil) | |
| fprint(2, "can't stat %s\n", file); | |
| else { | |
| scanobj(bfile, astart, d->length); | |
| armove(bfile, astart, newmember()); | |
| free(d); | |
| } | |
| Bterm(bfile); | |
| } | |
| } | |
| if(fd < 0 && !cflag) | |
| install(arname, astart, 0, aend, 1); /* issue 'creating' msg */ | |
| else | |
| install(arname, astart, 0, aend, 0); | |
| } | |
| void | |
| dcmd(char *arname, int count, char **files) | |
| { | |
| Armember *bp; | |
| int fd, i; | |
| if (!count) | |
| return; | |
| fd = openar(arname, ORDWR, 0); | |
| Binit(&bar, fd, OREAD); | |
| Bseek(&bar,seek(fd,0,1), 1); | |
| astart = newtempfile(artemp); | |
| for (i = 0; bp = getdir(&bar); i++) { | |
| if(match(count, files)) { | |
| mesg('d', file); | |
| skip(&bar, bp->size); | |
| if (strcmp(file, symdef) == 0) | |
| allobj = 0; | |
| } else if (i == 0 && strcmp(file, symdef) == 0) { | |
| skip(&bar, bp->size); | |
| } else if (gflag && strcmp(file, pkgdef) == 0) { | |
| skip(&bar, bp->size); | |
| } else { | |
| scanobj(&bar, astart, bp->size); | |
| arcopy(&bar, astart, bp); | |
| } | |
| } | |
| close(fd); | |
| install(arname, astart, 0, 0, 0); | |
| } | |
| void | |
| xcmd(char *arname, int count, char **files) | |
| { | |
| int fd, f, mode, i; | |
| Armember *bp; | |
| Dir dx; | |
| fd = openar(arname, OREAD, 0); | |
| Binit(&bar, fd, OREAD); | |
| Bseek(&bar,seek(fd,0,1), 1); | |
| i = 0; | |
| while (bp = getdir(&bar)) { | |
| if(count == 0 || match(count, files)) { | |
| mode = strtoul(bp->hdr.mode, 0, 8) & 0777; | |
| f = create(file, OWRITE, mode); | |
| if(f < 0) { | |
| fprint(2, "gopack: %s cannot create\n", file); | |
| skip(&bar, bp->size); | |
| } else { | |
| mesg('x', file); | |
| arcopy(&bar, 0, bp); | |
| if (write(f, bp->member, bp->size) < 0) | |
| wrerr(); | |
| if(oflag && bp->date != 0) { | |
| nulldir(&dx); | |
| dx.atime = bp->date; | |
| dx.mtime = bp->date; | |
| if(dirwstat(file, &dx) < 0) | |
| perror(file); | |
| } | |
| free(bp->member); | |
| close(f); | |
| } | |
| free(bp); | |
| if (count && ++i >= count) | |
| break; | |
| } else { | |
| skip(&bar, bp->size); | |
| free(bp); | |
| } | |
| } | |
| close(fd); | |
| } | |
| void | |
| pcmd(char *arname, int count, char **files) | |
| { | |
| int fd; | |
| Armember *bp; | |
| fd = openar(arname, OREAD, 0); | |
| Binit(&bar, fd, OREAD); | |
| Bseek(&bar,seek(fd,0,1), 1); | |
| while(bp = getdir(&bar)) { | |
| if(count == 0 || match(count, files)) { | |
| if(vflag) | |
| print("\n<%s>\n\n", file); | |
| arcopy(&bar, 0, bp); | |
| if (write(1, bp->member, bp->size) < 0) | |
| wrerr(); | |
| } else | |
| skip(&bar, bp->size); | |
| free(bp); | |
| } | |
| close(fd); | |
| } | |
| void | |
| mcmd(char *arname, int count, char **files) | |
| { | |
| int fd, i; | |
| Arfile *ap; | |
| Armember *bp; | |
| if (count == 0) | |
| return; | |
| fd = openar(arname, ORDWR, 0); | |
| Binit(&bar, fd, OREAD); | |
| Bseek(&bar,seek(fd,0,1), 1); | |
| astart = newtempfile(artemp); | |
| amiddle = newtempfile(movtemp); | |
| aend = 0; | |
| ap = astart; | |
| for (i = 0; bp = getdir(&bar); i++) { | |
| if (bamatch(file, poname)) { | |
| aend = newtempfile(tailtemp); | |
| ap = aend; | |
| } | |
| if(match(count, files)) { | |
| mesg('m', file); | |
| scanobj(&bar, amiddle, bp->size); | |
| arcopy(&bar, amiddle, bp); | |
| } else if (ap == astart && i == 0 && strcmp(file, symdef) == 0) { | |
| /* | |
| * pitch the symdef file if it is at the beginning | |
| * of the archive and we aren't inserting in front | |
| * of it (ap == astart). | |
| */ | |
| skip(&bar, bp->size); | |
| } else if (ap == astart && gflag && strcmp(file, pkgdef) == 0) { | |
| /* | |
| * pitch the pkgdef file if we aren't inserting in front | |
| * of it (ap == astart). | |
| */ | |
| skip(&bar, bp->size); | |
| } else { | |
| scanobj(&bar, ap, bp->size); | |
| arcopy(&bar, ap, bp); | |
| } | |
| } | |
| close(fd); | |
| if (poname[0] && aend == 0) | |
| fprint(2, "gopack: %s not found - files moved to end.\n", poname); | |
| install(arname, astart, amiddle, aend, 0); | |
| } | |
| void | |
| tcmd(char *arname, int count, char **files) | |
| { | |
| int fd; | |
| Armember *bp; | |
| char name[ARNAMESIZE+1]; | |
| fd = openar(arname, OREAD, 0); | |
| Binit(&bar, fd, OREAD); | |
| Bseek(&bar,seek(fd,0,1), 1); | |
| while(bp = getdir(&bar)) { | |
| if(count == 0 || match(count, files)) { | |
| if(vflag) | |
| longt(bp); | |
| trim(file, name, ARNAMESIZE); | |
| Bprint(&bout, "%s\n", name); | |
| } | |
| skip(&bar, bp->size); | |
| free(bp); | |
| } | |
| close(fd); | |
| } | |
| void | |
| qcmd(char *arname, int count, char **files) | |
| { | |
| int fd, i; | |
| Armember *bp; | |
| Biobuf *bfile; | |
| if(aflag || bflag) { | |
| fprint(2, "gopack: abi not allowed with q\n"); | |
| exits("error"); | |
| } | |
| fd = openar(arname, ORDWR, 1); | |
| if (fd < 0) { | |
| if(!cflag) | |
| fprint(2, "gopack: creating %s\n", arname); | |
| fd = arcreate(arname); | |
| } | |
| Binit(&bar, fd, OREAD); | |
| Bseek(&bar,seek(fd,0,1), 1); | |
| /* leave note group behind when writing archive; i.e. sidestep interrupts */ | |
| rfork(RFNOTEG); | |
| Bseek(&bar, 0, 2); | |
| bp = newmember(); | |
| for(i=0; i<count && files[i]; i++) { | |
| file = files[i]; | |
| files[i] = 0; | |
| bfile = Bopen(file, OREAD); | |
| if(!bfile) { | |
| fprint(2, "gopack: cannot open %s\n", file); | |
| errors++; | |
| } else { | |
| mesg('q', file); | |
| armove(bfile, 0, bp); | |
| if (!arwrite(fd, bp)) | |
| wrerr(); | |
| free(bp->member); | |
| bp->member = 0; | |
| Bterm(bfile); | |
| } | |
| } | |
| free(bp); | |
| close(fd); | |
| } | |
| /* | |
| * does the object header line p match the last one we saw? | |
| * update *lastp if it gets more specific. | |
| */ | |
| int | |
| matchhdr(char *p, char **lastp) | |
| { | |
| int n; | |
| char *last; | |
| // no information? | |
| last = *lastp; | |
| if(last == nil) { | |
| *lastp = strdup(p); | |
| return 1; | |
| } | |
| // identical match? | |
| if(strcmp(last, p) == 0) | |
| return 1; | |
| // last has extra fields | |
| n = strlen(p); | |
| if(n < strlen(last) && last[n] == ' ') | |
| return 1; | |
| // p has extra fields - save in last | |
| n = strlen(last); | |
| if(n < strlen(p) && p[n] == ' ') { | |
| free(last); | |
| *lastp = strdup(p); | |
| return 1; | |
| } | |
| return 0; | |
| } | |
| /* | |
| * extract the symbol references from an object file | |
| */ | |
| void | |
| scanobj(Biobuf *b, Arfile *ap, long size) | |
| { | |
| int obj, goobject; | |
| vlong offset, offset1; | |
| Dir *d; | |
| static int lastobj = -1; | |
| uchar buf[4]; | |
| char *p; | |
| if (!allobj) /* non-object file encountered */ | |
| return; | |
| offset = Boffset(b); | |
| obj = objtype(b, 0); | |
| if (obj < 0) { /* not an object file */ | |
| /* maybe a foreign object file */ | |
| Bseek(b, offset, 0); | |
| memset(buf, 0, sizeof buf); | |
| Bread(b, buf, 4); | |
| /* maybe a foreign object file? that's okay */ | |
| if((buf[0] == 0x7F && buf[1] == 'E' && buf[2] == 'L' && buf[3] == 'F') || // ELF | |
| (buf[0] == 0x4c && buf[1] == 0x01 || buf[0] == 0x64 && buf[1] == 0x86) || // Windows PE | |
| (buf[0] == 0xFE && buf[1] == 0xED && buf[2] == 0xFA && (buf[3]&~1) == 0xCE) || // Mach-O big-endian | |
| (buf[3] == 0xFE && buf[2] == 0xED && buf[1] == 0xFA && (buf[0]&~1) == 0xCE)) { // Mach-O little-endian | |
| Bseek(b, offset, 0); | |
| return; | |
| } | |
| if (!gflag || strcmp(file, pkgdef) != 0) { /* don't clear allobj if it's pkg defs */ | |
| fprint(2, "gopack: non-object file %s\n", file); | |
| errors++; | |
| allobj = 0; | |
| } | |
| d = dirfstat(Bfildes(b)); | |
| if (d != nil && d->length == 0) { | |
| fprint(2, "gopack: zero length file %s\n", file); | |
| errors++; | |
| } | |
| free(d); | |
| Bseek(b, offset, 0); | |
| return; | |
| } | |
| goobject = 1; | |
| offset1 = Boffset(b); | |
| Bseek(b, offset, 0); | |
| p = Brdstr(b, '\n', 1); | |
| // After the go object header comes the Go metadata, | |
| // followed by ! on a line by itself. If this is not a Go object, | |
| // the ! comes immediately. Catch that so we can avoid | |
| // the call to scanpkg below, since scanpkg assumes that the | |
| // Go metadata is present. | |
| if(Bgetc(b) == '!') | |
| goobject = 0; | |
| Bseek(b, offset1, 0); | |
| if(p == nil || strncmp(p, "go object ", 10) != 0) { | |
| fprint(2, "gopack: malformed object file %s\n", file); | |
| errors++; | |
| Bseek(b, offset, 0); | |
| free(p); | |
| return; | |
| } | |
| if (!matchhdr(p, &objhdr)) { | |
| fprint(2, "gopack: inconsistent object file %s: [%s] vs [%s]\n", file, p, objhdr); | |
| errors++; | |
| allobj = 0; | |
| free(p); | |
| return; | |
| } | |
| free(p); | |
| // Old check. Should be impossible since objhdrs match, but keep the check anyway. | |
| if (lastobj >= 0 && obj != lastobj) { | |
| fprint(2, "gopack: inconsistent object file %s\n", file); | |
| errors++; | |
| allobj = 0; | |
| return; | |
| } | |
| lastobj = obj; | |
| if (!readar(b, obj, offset+size, 0)) { | |
| fprint(2, "gopack: invalid symbol reference in file %s\n", file); | |
| errors++; | |
| allobj = 0; | |
| Bseek(b, offset, 0); | |
| return; | |
| } | |
| Bseek(b, offset, 0); | |
| objtraverse(objsym, ap); | |
| if (gflag && goobject) { | |
| scanpkg(b, size); | |
| Bseek(b, offset, 0); | |
| } | |
| } | |
| /* | |
| * does line contain substring (length-limited) | |
| */ | |
| int | |
| strstrn(char *line, int len, char *sub) | |
| { | |
| int i; | |
| int sublen; | |
| sublen = strlen(sub); | |
| for (i = 0; i < len - sublen; i++) | |
| if (memcmp(line+i, sub, sublen) == 0) | |
| return 1; | |
| return 0; | |
| } | |
| /* | |
| * package import data | |
| */ | |
| int safe = 1; | |
| char* pkgname; | |
| char* importblock; | |
| void | |
| getpkgdef(char **datap, int *lenp) | |
| { | |
| char *tag, *hdr; | |
| if(pkgname == nil) { | |
| pkgname = "__emptyarchive__"; | |
| importblock = ""; | |
| } | |
| tag = ""; | |
| if(safe || Sflag) | |
| tag = "safe"; | |
| hdr = "empty archive"; | |
| if(objhdr != nil) | |
| hdr = objhdr; | |
| *datap = smprint("%s\nimport\n$$\npackage %s %s\n%s\n$$\n", hdr, pkgname, tag, importblock); | |
| *lenp = strlen(*datap); | |
| } | |
| /* | |
| * extract the package definition data from an object file. | |
| * there can be only one. | |
| */ | |
| void | |
| scanpkg(Biobuf *b, long size) | |
| { | |
| long n; | |
| int c; | |
| long start, end, pkgsize; | |
| char *data, *line, pkgbuf[1024], *pkg; | |
| int first; | |
| /* | |
| * scan until $$ | |
| */ | |
| for (n=0; n<size; ) { | |
| c = Bgetc(b); | |
| if(c == Beof) | |
| break; | |
| n++; | |
| if(c != '$') | |
| continue; | |
| c = Bgetc(b); | |
| if(c == Beof) | |
| break; | |
| n++; | |
| if(c != '$') | |
| continue; | |
| goto foundstart; | |
| } | |
| // fprint(2, "gopack: warning: no package import section in %s\n", file); | |
| if(b != &bar || !pkgdefsafe) | |
| safe = 0; // non-Go file (C or assembly) | |
| return; | |
| foundstart: | |
| /* found $$; skip rest of line */ | |
| while((c = Bgetc(b)) != '\n') | |
| if(c == Beof) | |
| goto bad; | |
| /* how big is it? */ | |
| pkg = nil; | |
| first = 1; | |
| start = end = 0; | |
| for (n=0; n<size; n+=Blinelen(b)) { | |
| line = Brdstr(b, '\n', 0); | |
| if (line == nil) | |
| goto bad; | |
| if (first && strstrn(line, Blinelen(b), "package ")) { | |
| if (Blinelen(b) > sizeof(pkgbuf)-1) | |
| goto bad; | |
| memmove(pkgbuf, line, Blinelen(b)); | |
| pkgbuf[Blinelen(b)] = '\0'; | |
| pkg = pkgbuf; | |
| while(*pkg == ' ' || *pkg == '\t') | |
| pkg++; | |
| if(strncmp(pkg, "package ", 8) != 0) | |
| goto bad; | |
| pkg += 8; | |
| data = pkg; | |
| while(*pkg != ' ' && *pkg != '\n' && *pkg != '\0') | |
| pkg++; | |
| pkgname = armalloc(pkg - data + 1); | |
| memmove(pkgname, data, pkg - data); | |
| pkgname[pkg-data] = '\0'; | |
| if(strcmp(pkg, " safe\n") != 0 && (b != &bar || !pkgdefsafe)) | |
| safe = 0; | |
| start = Boffset(b); // after package statement | |
| first = 0; | |
| free(line); | |
| continue; | |
| } | |
| if(line[0] == '$' && line[1] == '$') { | |
| free(line); | |
| goto foundend; | |
| } | |
| end = Boffset(b); // before closing $$ | |
| free(line); | |
| } | |
| bad: | |
| fprint(2, "gopack: bad package import section in %s\n", file); | |
| errors++; | |
| return; | |
| foundend: | |
| if (start == 0) | |
| return; | |
| if (end == 0) | |
| goto bad; | |
| if(importblock != nil) { | |
| fprint(2, "gopack: multiple Go object files\n"); | |
| errors++; | |
| return; | |
| } | |
| pkgsize = end-start; | |
| data = armalloc(end - start + 1); | |
| Bseek(b, start, 0); | |
| if (Bread(b, data, pkgsize) != pkgsize) { | |
| fprint(2, "gopack: error reading package import section in %s\n", file); | |
| errors++; | |
| return; | |
| } | |
| data[end-start] = '\0'; | |
| importblock = data; | |
| } | |
| /* | |
| * add text and data symbols to the symbol list | |
| */ | |
| void | |
| objsym(Sym *s, void *p) | |
| { | |
| int n; | |
| Arsymref *as; | |
| Arfile *ap; | |
| char *ofile; | |
| if (s->type != 'T' && s->type != 'D') | |
| return; | |
| ap = (Arfile*)p; | |
| as = armalloc(sizeof(Arsymref)); | |
| as->offset = ap->size; | |
| as->name = arstrdup(s->name); | |
| as->file = arstrdup(file); | |
| if(s->type == 'T' && duplicate(as->name, &ofile)) { | |
| dupfound = 1; | |
| fprint(2, "duplicate text symbol: %s and %s: %s\n", as->file, ofile, as->name); | |
| errors++; | |
| free(as->name); | |
| free(as); | |
| return; | |
| } | |
| as->type = s->type; | |
| n = strlen(s->name); | |
| symdefsize += 4+(n+1)+1; | |
| as->len = n; | |
| as->next = ap->sym; | |
| ap->sym = as; | |
| } | |
| /* | |
| * Check the symbol table for duplicate text symbols | |
| */ | |
| int | |
| hashstr(char *name) | |
| { | |
| int h; | |
| char *cp; | |
| h = 0; | |
| for(cp = name; *cp; h += *cp++) | |
| h *= 1119; | |
| // the code used to say | |
| // if(h < 0) | |
| // h = ~h; | |
| // but on gcc 4.3 with -O2 on some systems, | |
| // the if(h < 0) gets compiled away as not possible. | |
| // use a mask instead, leaving plenty of bits but | |
| // definitely not the sign bit. | |
| return h & 0xfffffff; | |
| } | |
| int | |
| duplicate(char *name, char **ofile) | |
| { | |
| Hashchain *p; | |
| int h; | |
| h = hashstr(name) % NHASH; | |
| for(p = hash[h]; p; p = p->next) | |
| if(strcmp(p->name, name) == 0) { | |
| *ofile = p->file; | |
| return 1; | |
| } | |
| p = armalloc(sizeof(Hashchain)); | |
| p->next = hash[h]; | |
| p->name = name; | |
| p->file = file; | |
| hash[h] = p; | |
| *ofile = nil; | |
| return 0; | |
| } | |
| /* | |
| * open an archive and validate its header | |
| */ | |
| int | |
| openar(char *arname, int mode, int errok) | |
| { | |
| int fd; | |
| char mbuf[SARMAG]; | |
| fd = open(arname, mode); | |
| if(fd >= 0){ | |
| if(read(fd, mbuf, SARMAG) != SARMAG || strncmp(mbuf, ARMAG, SARMAG)) { | |
| fprint(2, "gopack: %s not in archive format\n", arname); | |
| exits("error"); | |
| } | |
| }else if(!errok){ | |
| fprint(2, "gopack: cannot open %s: %r\n", arname); | |
| exits("error"); | |
| } | |
| return fd; | |
| } | |
| /* | |
| * create an archive and set its header | |
| */ | |
| int | |
| arcreate(char *arname) | |
| { | |
| int fd; | |
| fd = create(arname, OWRITE, 0664); | |
| if(fd < 0){ | |
| fprint(2, "gopack: cannot create %s: %r\n", arname); | |
| exits("error"); | |
| } | |
| if(write(fd, ARMAG, SARMAG) != SARMAG) | |
| wrerr(); | |
| return fd; | |
| } | |
| /* | |
| * error handling | |
| */ | |
| void | |
| wrerr(void) | |
| { | |
| perror("gopack: write error"); | |
| exits("error"); | |
| } | |
| void | |
| rderr(void) | |
| { | |
| perror("gopack: read error"); | |
| exits("error"); | |
| } | |
| void | |
| phaseerr(int offset) | |
| { | |
| fprint(2, "gopack: phase error at offset %d\n", offset); | |
| exits("error"); | |
| } | |
| void | |
| usage(void) | |
| { | |
| fprint(2, "usage: gopack [%s][%s][P prefix] archive files ...\n", opt, man); | |
| exits("error"); | |
| } | |
| /* | |
| * read the header for the next archive member | |
| */ | |
| Armember * | |
| getdir(Biobuf *b) | |
| { | |
| Armember *bp; | |
| char *cp; | |
| static char name[ARNAMESIZE+1]; | |
| bp = newmember(); | |
| if(HEADER_IO(Bread, b, bp->hdr)) { | |
| free(bp); | |
| return 0; | |
| } | |
| if(strncmp(bp->hdr.fmag, ARFMAG, sizeof(bp->hdr.fmag))) | |
| phaseerr(Boffset(b)); | |
| strncpy(name, bp->hdr.name, sizeof(bp->hdr.name)); | |
| cp = name+sizeof(name)-1; | |
| while(*--cp==' ') | |
| ; | |
| cp[1] = '\0'; | |
| file = arstrdup(name); | |
| bp->date = strtol(bp->hdr.date, 0, 0); | |
| bp->size = strtol(bp->hdr.size, 0, 0); | |
| return bp; | |
| } | |
| /* | |
| * Copy the file referenced by fd to the temp file | |
| */ | |
| void | |
| armove(Biobuf *b, Arfile *ap, Armember *bp) | |
| { | |
| char *cp; | |
| Dir *d; | |
| vlong n; | |
| d = dirfstat(Bfildes(b)); | |
| if (d == nil) { | |
| fprint(2, "gopack: cannot stat %s\n", file); | |
| return; | |
| } | |
| trim(file, bp->hdr.name, sizeof(bp->hdr.name)); | |
| for (cp = strchr(bp->hdr.name, 0); /* blank pad on right */ | |
| cp < bp->hdr.name+sizeof(bp->hdr.name); cp++) | |
| *cp = ' '; | |
| sprint(bp->hdr.date, "%-12ld", 0); // was d->mtime but removed for idempotent builds | |
| sprint(bp->hdr.uid, "%-6d", 0); | |
| sprint(bp->hdr.gid, "%-6d", 0); | |
| sprint(bp->hdr.mode, "%-8lo", d->mode); | |
| sprint(bp->hdr.size, "%-10lld", d->length); | |
| strncpy(bp->hdr.fmag, ARFMAG, 2); | |
| bp->size = d->length; | |
| arread(b, bp); | |
| n = bp->size; | |
| if (n&1) | |
| n++; | |
| if (ap) { | |
| arinsert(ap, bp); | |
| ap->size += n+SAR_HDR; | |
| } | |
| free(d); | |
| } | |
| /* | |
| * Copy the archive member at the current offset into the temp file. | |
| */ | |
| void | |
| arcopy(Biobuf *b, Arfile *ap, Armember *bp) | |
| { | |
| long n; | |
| arread(b, bp); | |
| n = bp->size; | |
| if (n & 01) | |
| n++; | |
| if (ap) { | |
| arinsert(ap, bp); | |
| ap->size += n+SAR_HDR; | |
| } | |
| } | |
| /* | |
| * Skip an archive member | |
| */ | |
| void | |
| skip(Biobuf *bp, vlong len) | |
| { | |
| if (len & 01) | |
| len++; | |
| Bseek(bp, len, 1); | |
| } | |
| void | |
| checksafe(Biobuf *bp, vlong len) | |
| { | |
| char *p; | |
| vlong end; | |
| if (len & 01) | |
| len++; | |
| end = Boffset(bp) + len; | |
| p = Brdline(bp, '\n'); | |
| if(p == nil || strncmp(p, "go object ", 10) != 0) | |
| goto done; | |
| for(;;) { | |
| p = Brdline(bp, '\n'); | |
| if(p == nil || Boffset(bp) >= end) | |
| goto done; | |
| if(strncmp(p, "$$\n", 3) == 0) | |
| break; | |
| } | |
| p = Brdline(bp, '\n'); | |
| if(p == nil || Boffset(bp) > end) | |
| goto done; | |
| if(Blinelen(bp) > 8+6 && strncmp(p, "package ", 8) == 0 && strncmp(p+Blinelen(bp)-6, " safe\n", 6) == 0) | |
| pkgdefsafe = 1; | |
| done: | |
| Bseek(bp, end, 0); | |
| } | |
| /* | |
| * Stream the three temp files to an archive | |
| */ | |
| void | |
| install(char *arname, Arfile *astart, Arfile *amiddle, Arfile *aend, int createflag) | |
| { | |
| int fd; | |
| if(allobj && dupfound) { | |
| fprint(2, "%s not changed\n", arname); | |
| return; | |
| } | |
| /* leave note group behind when copying back; i.e. sidestep interrupts */ | |
| rfork(RFNOTEG); | |
| if(createflag) | |
| fprint(2, "gopack: creating %s\n", arname); | |
| fd = arcreate(arname); | |
| if(allobj) | |
| rl(fd); | |
| if (astart) { | |
| arstream(fd, astart); | |
| arfree(astart); | |
| } | |
| if (amiddle) { | |
| arstream(fd, amiddle); | |
| arfree(amiddle); | |
| } | |
| if (aend) { | |
| arstream(fd, aend); | |
| arfree(aend); | |
| } | |
| close(fd); | |
| } | |
| void | |
| rl(int fd) | |
| { | |
| Biobuf b; | |
| char *cp; | |
| struct ar_hdr a; | |
| long len; | |
| int headlen; | |
| char *pkgdefdata; | |
| int pkgdefsize; | |
| pkgdefdata = nil; | |
| pkgdefsize = 0; | |
| Binit(&b, fd, OWRITE); | |
| Bseek(&b,seek(fd,0,1), 0); | |
| len = symdefsize; | |
| if(len&01) | |
| len++; | |
| sprint(a.date, "%-12ld", 0); // time(0) | |
| sprint(a.uid, "%-6d", 0); | |
| sprint(a.gid, "%-6d", 0); | |
| sprint(a.mode, "%-8lo", 0644L); | |
| sprint(a.size, "%-10ld", len); | |
| strncpy(a.fmag, ARFMAG, 2); | |
| strcpy(a.name, symdef); | |
| for (cp = strchr(a.name, 0); /* blank pad on right */ | |
| cp < a.name+sizeof(a.name); cp++) | |
| *cp = ' '; | |
| if(HEADER_IO(Bwrite, &b, a)) | |
| wrerr(); | |
| headlen = Boffset(&b); | |
| len += headlen; | |
| if (gflag) { | |
| getpkgdef(&pkgdefdata, &pkgdefsize); | |
| len += SAR_HDR + pkgdefsize; | |
| if (len & 1) | |
| len++; | |
| } | |
| if (astart) { | |
| wrsym(&b, len, astart->sym); | |
| len += astart->size; | |
| } | |
| if(amiddle) { | |
| wrsym(&b, len, amiddle->sym); | |
| len += amiddle->size; | |
| } | |
| if(aend) | |
| wrsym(&b, len, aend->sym); | |
| if(symdefsize&0x01) | |
| Bputc(&b, 0); | |
| if (gflag) { | |
| len = pkgdefsize; | |
| sprint(a.date, "%-12ld", 0); // time(0) | |
| sprint(a.uid, "%-6d", 0); | |
| sprint(a.gid, "%-6d", 0); | |
| sprint(a.mode, "%-8lo", 0644L); | |
| sprint(a.size, "%-10ld", (len + 1) & ~1); | |
| strncpy(a.fmag, ARFMAG, 2); | |
| strcpy(a.name, pkgdef); | |
| for (cp = strchr(a.name, 0); /* blank pad on right */ | |
| cp < a.name+sizeof(a.name); cp++) | |
| *cp = ' '; | |
| if(HEADER_IO(Bwrite, &b, a)) | |
| wrerr(); | |
| if (Bwrite(&b, pkgdefdata, pkgdefsize) != pkgdefsize) | |
| wrerr(); | |
| if(len&0x01) | |
| Bputc(&b, 0); | |
| } | |
| Bterm(&b); | |
| } | |
| /* | |
| * Write the defined symbols to the symdef file | |
| */ | |
| void | |
| wrsym(Biobuf *bp, long offset, Arsymref *as) | |
| { | |
| int off; | |
| while(as) { | |
| Bputc(bp, as->type); | |
| off = as->offset+offset; | |
| Bputc(bp, off); | |
| Bputc(bp, off>>8); | |
| Bputc(bp, off>>16); | |
| Bputc(bp, off>>24); | |
| if (Bwrite(bp, as->name, as->len+1) != as->len+1) | |
| wrerr(); | |
| as = as->next; | |
| } | |
| } | |
| /* | |
| * Check if the archive member matches an entry on the command line. | |
| */ | |
| int | |
| match(int count, char **files) | |
| { | |
| int i; | |
| char name[ARNAMESIZE+1]; | |
| for(i=0; i<count; i++) { | |
| if(files[i] == 0) | |
| continue; | |
| trim(files[i], name, ARNAMESIZE); | |
| if(strncmp(name, file, ARNAMESIZE) == 0) { | |
| file = files[i]; | |
| files[i] = 0; | |
| return 1; | |
| } | |
| } | |
| return 0; | |
| } | |
| /* | |
| * compare the current member to the name of the pivot member | |
| */ | |
| int | |
| bamatch(char *file, char *pivot) | |
| { | |
| static int state = 0; | |
| switch(state) | |
| { | |
| case 0: /* looking for position file */ | |
| if (aflag) { | |
| if (strncmp(file, pivot, ARNAMESIZE) == 0) | |
| state = 1; | |
| } else if (bflag) { | |
| if (strncmp(file, pivot, ARNAMESIZE) == 0) { | |
| state = 2; /* found */ | |
| return 1; | |
| } | |
| } | |
| break; | |
| case 1: /* found - after previous file */ | |
| state = 2; | |
| return 1; | |
| case 2: /* already found position file */ | |
| break; | |
| } | |
| return 0; | |
| } | |
| /* | |
| * output a message, if 'v' option was specified | |
| */ | |
| void | |
| mesg(int c, char *file) | |
| { | |
| if(vflag) | |
| Bprint(&bout, "%c - %s\n", c, file); | |
| } | |
| /* | |
| * isolate file name by stripping leading directories and trailing slashes | |
| */ | |
| void | |
| trim(char *s, char *buf, int n) | |
| { | |
| char *p; | |
| for(;;) { | |
| p = strrchr(s, '/'); | |
| if (!p) { /* no slash in name */ | |
| strncpy(buf, s, n); | |
| return; | |
| } | |
| if (p[1] != 0) { /* p+1 is first char of file name */ | |
| strncpy(buf, p+1, n); | |
| return; | |
| } | |
| *p = 0; /* strip trailing slash */ | |
| } | |
| } | |
| /* | |
| * utilities for printing long form of 't' command | |
| */ | |
| #define SUID 04000 | |
| #define SGID 02000 | |
| #define ROWN 0400 | |
| #define WOWN 0200 | |
| #define XOWN 0100 | |
| #define RGRP 040 | |
| #define WGRP 020 | |
| #define XGRP 010 | |
| #define ROTH 04 | |
| #define WOTH 02 | |
| #define XOTH 01 | |
| #define STXT 01000 | |
| void | |
| longt(Armember *bp) | |
| { | |
| char *cp; | |
| pmode(strtoul(bp->hdr.mode, 0, 8)); | |
| Bprint(&bout, "%3ld/%1ld", strtol(bp->hdr.uid, 0, 0), strtol(bp->hdr.gid, 0, 0)); | |
| Bprint(&bout, "%7ld", bp->size); | |
| cp = ctime(bp->date); | |
| Bprint(&bout, " %-12.12s %-4.4s ", cp+4, cp+24); | |
| } | |
| int m1[] = { 1, ROWN, 'r', '-' }; | |
| int m2[] = { 1, WOWN, 'w', '-' }; | |
| int m3[] = { 2, SUID, 's', XOWN, 'x', '-' }; | |
| int m4[] = { 1, RGRP, 'r', '-' }; | |
| int m5[] = { 1, WGRP, 'w', '-' }; | |
| int m6[] = { 2, SGID, 's', XGRP, 'x', '-' }; | |
| int m7[] = { 1, ROTH, 'r', '-' }; | |
| int m8[] = { 1, WOTH, 'w', '-' }; | |
| int m9[] = { 2, STXT, 't', XOTH, 'x', '-' }; | |
| int *m[] = { m1, m2, m3, m4, m5, m6, m7, m8, m9}; | |
| void | |
| pmode(long mode) | |
| { | |
| int **mp; | |
| for(mp = &m[0]; mp < &m[9];) | |
| select(*mp++, mode); | |
| } | |
| void | |
| select(int *ap, long mode) | |
| { | |
| int n; | |
| n = *ap++; | |
| while(--n>=0 && (mode&*ap++)==0) | |
| ap++; | |
| Bputc(&bout, *ap); | |
| } | |
| /* | |
| * Temp file I/O subsystem. We attempt to cache all three temp files in | |
| * core. When we run out of memory we spill to disk. | |
| * The I/O model assumes that temp files: | |
| * 1) are only written on the end | |
| * 2) are only read from the beginning | |
| * 3) are only read after all writing is complete. | |
| * The architecture uses one control block per temp file. Each control | |
| * block anchors a chain of buffers, each containing an archive member. | |
| */ | |
| Arfile * | |
| newtempfile(char *name) /* allocate a file control block */ | |
| { | |
| Arfile *ap; | |
| ap = armalloc(sizeof(Arfile)); | |
| ap->fname = name; | |
| return ap; | |
| } | |
| Armember * | |
| newmember(void) /* allocate a member buffer */ | |
| { | |
| return armalloc(sizeof(Armember)); | |
| } | |
| void | |
| arread(Biobuf *b, Armember *bp) /* read an image into a member buffer */ | |
| { | |
| int i; | |
| vlong off; | |
| bp->member = armalloc(bp->size); | |
| // If P flag is set, let arread_cutprefix try. | |
| // If it succeeds, we're done. If not, fall back | |
| // to a direct copy. | |
| off = Boffset(b); | |
| if(Pflag && arread_cutprefix(b, bp)) | |
| return; | |
| Bseek(b, off, 0); | |
| i = Bread(b, bp->member, bp->size); | |
| if (i < 0) { | |
| free(bp->member); | |
| bp->member = 0; | |
| rderr(); | |
| } | |
| if(bp->size&1) | |
| Bgetc(b); | |
| } | |
| /* | |
| * insert a member buffer into the member chain | |
| */ | |
| void | |
| arinsert(Arfile *ap, Armember *bp) | |
| { | |
| bp->next = 0; | |
| if (!ap->tail) | |
| ap->head = bp; | |
| else | |
| ap->tail->next = bp; | |
| ap->tail = bp; | |
| } | |
| /* | |
| * stream the members in a temp file to the file referenced by 'fd'. | |
| */ | |
| void | |
| arstream(int fd, Arfile *ap) | |
| { | |
| Armember *bp; | |
| int i; | |
| char buf[8192]; | |
| if (ap->paged) { /* copy from disk */ | |
| seek(ap->fd, 0, 0); | |
| for (;;) { | |
| i = read(ap->fd, buf, sizeof(buf)); | |
| if (i < 0) | |
| rderr(); | |
| if (i == 0) | |
| break; | |
| if (write(fd, buf, i) != i) | |
| wrerr(); | |
| } | |
| close(ap->fd); | |
| ap->paged = 0; | |
| } | |
| /* dump the in-core buffers */ | |
| for (bp = ap->head; bp; bp = bp->next) { | |
| if (!arwrite(fd, bp)) | |
| wrerr(); | |
| } | |
| } | |
| /* | |
| * write a member to 'fd'. | |
| */ | |
| int | |
| arwrite(int fd, Armember *bp) | |
| { | |
| int len; | |
| if(HEADER_IO(write, fd, bp->hdr)) | |
| return 0; | |
| len = bp->size; | |
| if (len & 01) | |
| len++; | |
| if (write(fd, bp->member, len) != len) | |
| return 0; | |
| return 1; | |
| } | |
| /* | |
| * Spill a member to a disk copy of a temp file | |
| */ | |
| int | |
| page(Arfile *ap) | |
| { | |
| USED(ap); | |
| sysfatal("page"); | |
| return 1; | |
| } | |
| /* | |
| * try to reclaim space by paging. we try to spill the start, middle, | |
| * and end files, in that order. there is no particular reason for the | |
| * ordering. | |
| */ | |
| int | |
| getspace(void) | |
| { | |
| fprint(2, "IN GETSPACE\n"); | |
| if (astart && astart->head && page(astart)) | |
| return 1; | |
| if (amiddle && amiddle->head && page(amiddle)) | |
| return 1; | |
| if (aend && aend->head && page(aend)) | |
| return 1; | |
| return 0; | |
| } | |
| void | |
| arfree(Arfile *ap) /* free a member buffer */ | |
| { | |
| Armember *bp, *next; | |
| for (bp = ap->head; bp; bp = next) { | |
| next = bp->next; | |
| if (bp->member) | |
| free(bp->member); | |
| free(bp); | |
| } | |
| free(ap); | |
| } | |
| /* | |
| * allocate space for a control block or member buffer. if the malloc | |
| * fails we try to reclaim space by spilling previously allocated | |
| * member buffers. | |
| */ | |
| void * | |
| armalloc(int n) | |
| { | |
| char *cp; | |
| // bump so that arwrite can do the same | |
| if(n&1) | |
| n++; | |
| do { | |
| cp = malloc(n); | |
| if (cp) { | |
| memset(cp, 0, n); | |
| return cp; | |
| } | |
| } while (getspace()); | |
| fprint(2, "gopack: out of memory\n"); | |
| exits("malloc"); | |
| return 0; | |
| } | |
| char * | |
| arstrdup(char *s) | |
| { | |
| char *t; | |
| t = armalloc(strlen(s) + 1); | |
| strcpy(t, s); | |
| return t; | |
| } | |
| /* | |
| * Parts of libmach we're not supposed | |
| * to look at but need for arread_cutprefix. | |
| */ | |
| extern int _read5(Biobuf*, Prog*); | |
| extern int _read6(Biobuf*, Prog*); | |
| extern int _read8(Biobuf*, Prog*); | |
| int (*reader[256])(Biobuf*, Prog*) = { | |
| [ObjArm] = _read5, | |
| [ObjAmd64] = _read6, | |
| [Obj386] = _read8, | |
| }; | |
| /* | |
| * copy b into bp->member but rewrite object | |
| * during copy to drop prefix from all file names. | |
| * return 1 if b was recognized as an object file | |
| * and copied successfully, 0 otherwise. | |
| */ | |
| int | |
| arread_cutprefix(Biobuf *b, Armember *bp) | |
| { | |
| vlong offset, o, end; | |
| int n, t; | |
| int (*rd)(Biobuf*, Prog*); | |
| char *w, *inprefix; | |
| Prog p; | |
| offset = Boffset(b); | |
| end = offset + bp->size; | |
| t = objtype(b, nil); | |
| if(t < 0) | |
| return 0; | |
| if((rd = reader[t]) == nil) | |
| return 0; | |
| // copy header | |
| w = bp->member; | |
| n = Boffset(b) - offset; | |
| Bseek(b, -n, 1); | |
| if(Bread(b, w, n) != n) | |
| return 0; | |
| offset += n; | |
| w += n; | |
| // read object file one pseudo-instruction at a time, | |
| // eliding the file name instructions that refer to | |
| // the prefix. | |
| memset(&p, 0, sizeof p); | |
| inprefix = nil; | |
| while(Boffset(b) < end && rd(b, &p)) { | |
| if(p.kind == aName && p.type == UNKNOWN && p.sym == 1 && p.id[0] == '<') { | |
| // part of a file path. | |
| // we'll keep continuing (skipping the copy) | |
| // around the loop until either we get to a | |
| // name piece that should be kept or we see | |
| // the whole prefix. | |
| if(inprefix == nil && prefix[0] == '/' && p.id[1] == '/' && p.id[2] == '\0') { | |
| // leading / | |
| inprefix = prefix+1; | |
| } else if(inprefix != nil) { | |
| // handle subsequent elements | |
| n = strlen(p.id+1); | |
| if(strncmp(p.id+1, inprefix, n) == 0 && (inprefix[n] == '/' || inprefix[n] == '\0')) { | |
| inprefix += n; | |
| if(inprefix[0] == '/') | |
| inprefix++; | |
| } | |
| } | |
| if(inprefix && inprefix[0] == '\0') { | |
| // reached end of prefix. | |
| // if we another path element follows, | |
| // nudge the offset to skip over the prefix we saw. | |
| // if not, leave offset alone, to emit the whole name. | |
| // additional name elements will not be skipped | |
| // because inprefix is now nil and we won't see another | |
| // leading / in this name. | |
| inprefix = nil; | |
| o = Boffset(b); | |
| if(o < end && rd(b, &p) && p.kind == aName && p.type == UNKNOWN && p.sym == 1 && p.id[0] == '<') { | |
| // print("skip %lld-%lld\n", offset, o); | |
| offset = o; | |
| } | |
| } | |
| } else { | |
| // didn't find the whole prefix. | |
| // give up and let it emit the entire name. | |
| inprefix = nil; | |
| } | |
| // copy instructions | |
| if(!inprefix) { | |
| n = Boffset(b) - offset; | |
| Bseek(b, -n, 1); | |
| if(Bread(b, w, n) != n) | |
| return 0; | |
| offset += n; | |
| w += n; | |
| } | |
| } | |
| bp->size = w - (char*)bp->member; | |
| sprint(bp->hdr.size, "%-10lld", (vlong)bp->size); | |
| strncpy(bp->hdr.fmag, ARFMAG, 2); | |
| Bseek(b, end, 0); | |
| if(Boffset(b)&1) | |
| Bgetc(b); | |
| return 1; | |
| } |