github.com/xushiwei/go@v0.0.0-20130601165731-2b9d83f45bc9/src/cmd/pack/ar.c (about)

     1  // Inferno utils/iar/ar.c
     2  // http://code.google.com/p/inferno-os/source/browse/utils/iar/ar.c
     3  //
     4  //	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
     5  //	Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
     6  //	Portions Copyright © 1997-1999 Vita Nuova Limited
     7  //	Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
     8  //	Portions Copyright © 2004,2006 Bruce Ellis
     9  //	Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
    10  //	Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
    11  //	Portions Copyright © 2009 The Go Authors. All rights reserved.
    12  //
    13  // Permission is hereby granted, free of charge, to any person obtaining a copy
    14  // of this software and associated documentation files (the "Software"), to deal
    15  // in the Software without restriction, including without limitation the rights
    16  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    17  // copies of the Software, and to permit persons to whom the Software is
    18  // furnished to do so, subject to the following conditions:
    19  //
    20  // The above copyright notice and this permission notice shall be included in
    21  // all copies or substantial portions of the Software.
    22  //
    23  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    24  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    25  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
    26  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    27  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    28  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    29  // THE SOFTWARE.
    30  
    31  /*
    32   * ar - portable (ascii) format version
    33   */
    34  
    35  /* protect a couple of our names */
    36  #define select your_select
    37  #define rcmd your_rcmd
    38  
    39  #include <u.h>
    40  #include <libc.h>
    41  #include <bio.h>
    42  #include <mach.h>
    43  #include "../../libmach/obj.h"
    44  #include <ar.h>
    45  
    46  #undef select
    47  #undef rcmd
    48  
    49  /*
    50   *	The algorithm uses up to 3 temp files.  The "pivot member" is the
    51   *	archive member specified by and a, b, or i option.  The temp files are
    52   *	astart - contains existing members up to and including the pivot member.
    53   *	amiddle - contains new files moved or inserted behind the pivot.
    54   *	aend - contains the existing members that follow the pivot member.
    55   *	When all members have been processed, function 'install' streams the
    56   * 	temp files, in order, back into the archive.
    57   */
    58  
    59  typedef struct	Arsymref
    60  {
    61  	char	*name;
    62  	char *file;
    63  	int	type;
    64  	int	len;
    65  	vlong	offset;
    66  	struct	Arsymref *next;
    67  } Arsymref;
    68  
    69  typedef struct	Armember	/* Temp file entry - one per archive member */
    70  {
    71  	struct Armember	*next;
    72  	struct ar_hdr	hdr;
    73  	long		size;
    74  	long		date;
    75  	void		*member;
    76  } Armember;
    77  
    78  typedef	struct Arfile		/* Temp file control block - one per tempfile */
    79  {
    80  	char	*fname;		/* paging file name */
    81  	vlong	size;
    82  	Armember *head;		/* head of member chain */
    83  	Armember *tail;		/* tail of member chain */
    84  	Arsymref *sym;		/* head of defined symbol chain */
    85  } Arfile;
    86  
    87  typedef struct Hashchain
    88  {
    89  	char	*name;
    90  	char *file;
    91  	struct Hashchain *next;
    92  } Hashchain;
    93  
    94  #define	NHASH	1024
    95  
    96  /*
    97   *	macro to portably read/write archive header.
    98   *	'cmd' is read/write/Bread/Bwrite, etc.
    99   */
   100  #define	HEADER_IO(cmd, f, h)	cmd(f, h.name, sizeof(h.name)) != sizeof(h.name)\
   101  				|| cmd(f, h.date, sizeof(h.date)) != sizeof(h.date)\
   102  				|| cmd(f, h.uid, sizeof(h.uid)) != sizeof(h.uid)\
   103  				|| cmd(f, h.gid, sizeof(h.gid)) != sizeof(h.gid)\
   104  				|| cmd(f, h.mode, sizeof(h.mode)) != sizeof(h.mode)\
   105  				|| cmd(f, h.size, sizeof(h.size)) != sizeof(h.size)\
   106  				|| cmd(f, h.fmag, sizeof(h.fmag)) != sizeof(h.fmag)
   107  
   108  		/* constants and flags */
   109  char	*man =		"mrxtdpq";
   110  char	*opt =		"uvnbailogS";
   111  char	artemp[] =	"/tmp/vXXXXX";
   112  char	movtemp[] =	"/tmp/v1XXXXX";
   113  char	tailtemp[] =	"/tmp/v2XXXXX";
   114  char	symdef[] =	"__.GOSYMDEF";
   115  char	pkgdef[] =	"__.PKGDEF";
   116  
   117  int	aflag;				/* command line flags */
   118  int	bflag;
   119  int	cflag;
   120  int	gflag;
   121  int	oflag;
   122  int	uflag;
   123  int	vflag;
   124  int	Pflag;	/* remove leading file prefix */
   125  int	Sflag;	/* force mark Go package as safe */
   126  
   127  int	errors;
   128  
   129  Arfile *astart, *amiddle, *aend;	/* Temp file control block pointers */
   130  int	allobj = 1;			/* set when all members are object files of the same type */
   131  int	symdefsize;			/* size of symdef file */
   132  char	*pkgstmt;		/* string "package foo" */
   133  char	*objhdr;		/* string "go object darwin 386 release.2010-01-01 2345+" */
   134  int	dupfound;			/* flag for duplicate symbol */
   135  Hashchain	*hash[NHASH];		/* hash table of text symbols */
   136  
   137  #define	ARNAMESIZE	sizeof(astart->tail->hdr.name)
   138  
   139  char	poname[ARNAMESIZE+1];		/* name of pivot member */
   140  char	*file;				/* current file or member being worked on */
   141  Biobuf	bout;
   142  Biobuf bar;
   143  char	*prefix;
   144  int	pkgdefsafe;		/* was __.PKGDEF marked safe? */
   145  
   146  void	arcopy(Biobuf*, Arfile*, Armember*);
   147  int	arcreate(char*);
   148  void	arfree(Arfile*);
   149  void	arinsert(Arfile*, Armember*);
   150  void	*armalloc(int);
   151  char *arstrdup(char*);
   152  void	armove(Biobuf*, Arfile*, Armember*);
   153  void	arread(Biobuf*, Armember*);
   154  void	arstream(int, Arfile*);
   155  int	arwrite(int, Armember*);
   156  int	bamatch(char*, char*);
   157  int	duplicate(char*, char**);
   158  Armember *getdir(Biobuf*);
   159  void	getpkgdef(char**, int*);
   160  void	install(char*, Arfile*, Arfile*, Arfile*, int);
   161  void	loadpkgdata(char*, int);
   162  void	longt(Armember*);
   163  int	match(int, char**);
   164  void	mesg(int, char*);
   165  Arfile	*newtempfile(char*);
   166  Armember *newmember(void);
   167  void	objsym(Sym*, void*);
   168  int	openar(char*, int, int);
   169  void	pmode(long);
   170  void	rl(int);
   171  void	scanobj(Biobuf*, Arfile*, long);
   172  void	scanpkg(Biobuf*, long);
   173  void	select(int*, long);
   174  void	setcom(void(*)(char*, int, char**));
   175  void	skip(Biobuf*, vlong);
   176  void	checksafe(Biobuf*, vlong);
   177  int	symcomp(void*, void*);
   178  void	trim(char*, char*, int);
   179  void	usage(void);
   180  void	wrerr(void);
   181  void	wrsym(Biobuf*, long, Arsymref*);
   182  int	arread_cutprefix(Biobuf*, Armember*);
   183  
   184  void	rcmd(char*, int, char**);		/* command processing */
   185  void	dcmd(char*, int, char**);
   186  void	xcmd(char*, int, char**);
   187  void	tcmd(char*, int, char**);
   188  void	pcmd(char*, int, char**);
   189  void	mcmd(char*, int, char**);
   190  void	qcmd(char*, int, char**);
   191  void	(*comfun)(char*, int, char**);
   192  
   193  void
   194  main(int argc, char *argv[])
   195  {
   196  	char *cp;
   197  
   198  	Binit(&bout, 1, OWRITE);
   199  	if(argc < 3)
   200  		usage();
   201  	for (cp = argv[1]; *cp; cp++) {
   202  		switch(*cp) {
   203  		case 'a':	aflag = 1;	break;
   204  		case 'b':	bflag = 1;	break;
   205  		case 'c':	cflag = 1;	break;
   206  		case 'd':	setcom(dcmd);	break;
   207  		case 'g':	gflag = 1; break;
   208  		case 'i':	bflag = 1;	break;
   209  		case 'l':
   210  				strcpy(artemp, "vXXXXX");
   211  				strcpy(movtemp, "v1XXXXX");
   212  				strcpy(tailtemp, "v2XXXXX");
   213  				break;
   214  		case 'm':	setcom(mcmd);	break;
   215  		case 'o':	oflag = 1;	break;
   216  		case 'p':	setcom(pcmd);	break;
   217  		case 'q':	setcom(qcmd);	break;
   218  		case 'r':	setcom(rcmd);	break;
   219  		case 't':	setcom(tcmd);	break;
   220  		case 'u':	uflag = 1;	break;
   221  		case 'v':	vflag = 1;	break;
   222  		case 'x':	setcom(xcmd);	break;
   223  		case 'S':	Sflag = 1;  break;
   224  		case 'P':	Pflag = 1;  break;
   225  		default:
   226  			fprint(2, "pack: bad option `%c'\n", *cp);
   227  			exits("error");
   228  		}
   229  	}
   230  	if (aflag && bflag) {
   231  		fprint(2, "pack: only one of 'a' and 'b' can be specified\n");
   232  		usage();
   233  	}
   234  	if(aflag || bflag) {
   235  		trim(argv[2], poname, sizeof(poname));
   236  		argv++;
   237  		argc--;
   238  		if(argc < 3)
   239  			usage();
   240  	}
   241  	if(Pflag) {
   242  		if(argc < 4) {
   243  			fprint(2, "pack: P flag requires prefix argument\n");
   244  			usage();
   245  		}
   246  		prefix = argv[2];
   247  		argv++;
   248  		argc--;
   249  	}
   250  	if(comfun == 0) {
   251  		if(uflag == 0) {
   252  			fprint(2, "pack: one of [%s] must be specified\n", man);
   253  			usage();
   254  		}
   255  		setcom(rcmd);
   256  	}
   257  	cp = argv[2];
   258  	argc -= 3;
   259  	argv += 3;
   260  	(*comfun)(cp, argc, argv);	/* do the command */
   261  	if(errors && cflag)
   262  		remove(cp);
   263  	cp = 0;
   264  	while (argc--) {
   265  		if (*argv) {
   266  			fprint(2, "pack: %s not found\n", *argv);
   267  			cp = "error";
   268  		}
   269  		argv++;
   270  	}
   271  	if (errors)
   272  		cp = "error";
   273  	exits(cp);
   274  }
   275  /*
   276   *	select a command
   277   */
   278  void
   279  setcom(void (*fun)(char *, int, char**))
   280  {
   281  
   282  	if(comfun != 0) {
   283  		fprint(2, "pack: only one of [%s] allowed\n", man);
   284  		usage();
   285  	}
   286  	comfun = fun;
   287  }
   288  /*
   289   *	perform the 'r' and 'u' commands
   290   */
   291  void
   292  rcmd(char *arname, int count, char **files)
   293  {
   294  	int fd;
   295  	int i;
   296  	Arfile *ap;
   297  	Armember *bp;
   298  	Dir *d;
   299  	Biobuf *bfile;
   300  
   301  	fd = openar(arname, ORDWR, 1);
   302  	if (fd >= 0) {
   303  		Binit(&bar, fd, OREAD);
   304  		Bseek(&bar,seek(fd,0,1), 1);
   305  	}
   306  	astart = newtempfile(artemp);
   307  	ap = astart;
   308  	aend = 0;
   309  	for(i = 0; fd >= 0; i++) {
   310  		bp = getdir(&bar);
   311  		if (!bp)
   312  			break;
   313  		if (bamatch(file, poname)) {		/* check for pivot */
   314  			aend = newtempfile(tailtemp);
   315  			ap = aend;
   316  		}
   317  			/* pitch symdef file */
   318  		if (i == 0 && strcmp(file, symdef) == 0) {
   319  			skip(&bar, bp->size);
   320  			continue;
   321  		}
   322  			/* pitch pkgdef file but remember whether it was marked safe */
   323  		if (gflag && strcmp(file, pkgdef) == 0) {
   324  			checksafe(&bar, bp->size);
   325  			continue;
   326  		}
   327  		/*
   328  		 * the plan 9 ar treats count == 0 as equivalent
   329  		 * to listing all the archive's files on the command line:
   330  		 * it will try to open every file name in the archive
   331  		 * and copy that file into the archive if it exists.
   332  		 * for go we disable that behavior, because we use
   333  		 * r with no files to make changes to the archive itself,
   334  		 * using the S or P flags.
   335  		 */
   336  		if (!match(count, files)) {
   337  			scanobj(&bar, ap, bp->size);
   338  			arcopy(&bar, ap, bp);
   339  			continue;
   340  		}
   341  		bfile = Bopen(file, OREAD);
   342  		if (!bfile) {
   343  			if (count != 0) {
   344  				fprint(2, "pack: cannot open %s\n", file);
   345  				errors++;
   346  			}
   347  			scanobj(&bar, ap, bp->size);
   348  			arcopy(&bar, ap, bp);
   349  			continue;
   350  		}
   351  		d = dirfstat(Bfildes(bfile));
   352  		if(d == nil)
   353  			fprint(2, "pack: cannot stat %s: %r\n", file);
   354  		if (uflag && (d==nil || d->mtime <= bp->date)) {
   355  			scanobj(&bar, ap, bp->size);
   356  			arcopy(&bar, ap, bp);
   357  			Bterm(bfile);
   358  			free(d);
   359  			continue;
   360  		}
   361  		mesg('r', file);
   362  		skip(&bar, bp->size);
   363  		scanobj(bfile, ap, d->length);
   364  		free(d);
   365  		armove(bfile, ap, bp);
   366  		Bterm(bfile);
   367  	}
   368  	if(fd >= 0)
   369  		close(fd);
   370  		/* copy in remaining files named on command line */
   371  	for (i = 0; i < count; i++) {
   372  		file = files[i];
   373  		if(file == 0)
   374  			continue;
   375  		files[i] = 0;
   376  		bfile = Bopen(file, OREAD);
   377  		if (!bfile) {
   378  			fprint(2, "pack: cannot open %s\n", file);
   379  			errors++;
   380  		} else {
   381  			mesg('a', file);
   382  			d = dirfstat(Bfildes(bfile));
   383  			if (d == nil)
   384  				fprint(2, "can't stat %s\n", file);
   385  			else {
   386  				scanobj(bfile, astart, d->length);
   387  				armove(bfile, astart, newmember());
   388  				free(d);
   389  			}
   390  			Bterm(bfile);
   391  		}
   392  	}
   393  	if(fd < 0 && !cflag)
   394  		install(arname, astart, 0, aend, 1);	/* issue 'creating' msg */
   395  	else
   396  		install(arname, astart, 0, aend, 0);
   397  }
   398  
   399  void
   400  dcmd(char *arname, int count, char **files)
   401  {
   402  	Armember *bp;
   403  	int fd, i;
   404  
   405  	if (!count)
   406  		return;
   407  	fd = openar(arname, ORDWR, 0);
   408  	Binit(&bar, fd, OREAD);
   409  	Bseek(&bar,seek(fd,0,1), 1);
   410  	astart = newtempfile(artemp);
   411  	for (i = 0; bp = getdir(&bar); i++) {
   412  		if(match(count, files)) {
   413  			mesg('d', file);
   414  			skip(&bar, bp->size);
   415  			if (strcmp(file, symdef) == 0)
   416  				allobj = 0;
   417  		} else if (i == 0 && strcmp(file, symdef) == 0) {
   418  			skip(&bar, bp->size);
   419  		} else if (gflag && strcmp(file, pkgdef) == 0) {
   420  			skip(&bar, bp->size);
   421  		} else {
   422  			scanobj(&bar, astart, bp->size);
   423  			arcopy(&bar, astart, bp);
   424  		}
   425  	}
   426  	close(fd);
   427  	install(arname, astart, 0, 0, 0);
   428  }
   429  
   430  void
   431  xcmd(char *arname, int count, char **files)
   432  {
   433  	int fd, f, mode, i;
   434  	Armember *bp;
   435  	Dir dx;
   436  
   437  	fd = openar(arname, OREAD, 0);
   438  	Binit(&bar, fd, OREAD);
   439  	Bseek(&bar,seek(fd,0,1), 1);
   440  	i = 0;
   441  	while (bp = getdir(&bar)) {
   442  		if(count == 0 || match(count, files)) {
   443  			mode = strtoul(bp->hdr.mode, 0, 8) & 0777;
   444  			f = create(file, OWRITE, mode);
   445  			if(f < 0) {
   446  				fprint(2, "pack: %s cannot create\n", file);
   447  				skip(&bar, bp->size);
   448  			} else {
   449  				mesg('x', file);
   450  				arcopy(&bar, 0, bp);
   451  				if (write(f, bp->member, bp->size) < 0)
   452  					wrerr();
   453  				if(oflag && bp->date != 0) {
   454  					nulldir(&dx);
   455  					dx.atime = bp->date;
   456  					dx.mtime = bp->date;
   457  					if(dirwstat(file, &dx) < 0)
   458  						perror(file);
   459  				}
   460  				free(bp->member);
   461  				close(f);
   462  			}
   463  			free(bp);
   464  			if (count && ++i >= count)
   465  				break;
   466  		} else {
   467  			skip(&bar, bp->size);
   468  			free(bp);
   469  		}
   470  	}
   471  	close(fd);
   472  }
   473  void
   474  pcmd(char *arname, int count, char **files)
   475  {
   476  	int fd;
   477  	Armember *bp;
   478  
   479  	fd = openar(arname, OREAD, 0);
   480  	Binit(&bar, fd, OREAD);
   481  	Bseek(&bar,seek(fd,0,1), 1);
   482  	while(bp = getdir(&bar)) {
   483  		if(count == 0 || match(count, files)) {
   484  			if(vflag)
   485  				print("\n<%s>\n\n", file);
   486  			arcopy(&bar, 0, bp);
   487  			if (write(1, bp->member, bp->size) < 0)
   488  				wrerr();
   489  		} else
   490  			skip(&bar, bp->size);
   491  		free(bp);
   492  	}
   493  	close(fd);
   494  }
   495  void
   496  mcmd(char *arname, int count, char **files)
   497  {
   498  	int fd, i;
   499  	Arfile *ap;
   500  	Armember *bp;
   501  
   502  	if (count == 0)
   503  		return;
   504  	fd = openar(arname, ORDWR, 0);
   505  	Binit(&bar, fd, OREAD);
   506  	Bseek(&bar,seek(fd,0,1), 1);
   507  	astart = newtempfile(artemp);
   508  	amiddle = newtempfile(movtemp);
   509  	aend = 0;
   510  	ap = astart;
   511  	for (i = 0; bp = getdir(&bar); i++) {
   512  		if (bamatch(file, poname)) {
   513  			aend = newtempfile(tailtemp);
   514  			ap = aend;
   515  		}
   516  		if(match(count, files)) {
   517  			mesg('m', file);
   518  			scanobj(&bar, amiddle, bp->size);
   519  			arcopy(&bar, amiddle, bp);
   520  		} else if (ap == astart && i == 0 && strcmp(file, symdef) == 0) {
   521  			/*
   522  			 * pitch the symdef file if it is at the beginning
   523  			 * of the archive and we aren't inserting in front
   524  			 * of it (ap == astart).
   525  			 */
   526  			skip(&bar, bp->size);
   527  		} else if (ap == astart && gflag && strcmp(file, pkgdef) == 0) {
   528  			/*
   529  			 * pitch the pkgdef file if we aren't inserting in front
   530  			 * of it (ap == astart).
   531  			 */
   532  			skip(&bar, bp->size);
   533  		} else {
   534  			scanobj(&bar, ap, bp->size);
   535  			arcopy(&bar, ap, bp);
   536  		}
   537  	}
   538  	close(fd);
   539  	if (poname[0] && aend == 0)
   540  		fprint(2, "pack: %s not found - files moved to end.\n", poname);
   541  	install(arname, astart, amiddle, aend, 0);
   542  }
   543  void
   544  tcmd(char *arname, int count, char **files)
   545  {
   546  	int fd;
   547  	Armember *bp;
   548  	char name[ARNAMESIZE+1];
   549  
   550  	fd = openar(arname, OREAD, 0);
   551  	Binit(&bar, fd, OREAD);
   552  	Bseek(&bar,seek(fd,0,1), 1);
   553  	while(bp = getdir(&bar)) {
   554  		if(count == 0 || match(count, files)) {
   555  			if(vflag)
   556  				longt(bp);
   557  			trim(file, name, ARNAMESIZE);
   558  			Bprint(&bout, "%s\n", name);
   559  		}
   560  		skip(&bar, bp->size);
   561  		free(bp);
   562  	}
   563  	close(fd);
   564  }
   565  void
   566  qcmd(char *arname, int count, char **files)
   567  {
   568  	int fd, i;
   569  	Armember *bp;
   570  	Biobuf *bfile;
   571  
   572  	if(aflag || bflag) {
   573  		fprint(2, "pack: abi not allowed with q\n");
   574  		exits("error");
   575  	}
   576  	fd = openar(arname, ORDWR, 1);
   577  	if (fd < 0) {
   578  		if(!cflag)
   579  			fprint(2, "pack: creating %s\n", arname);
   580  		fd = arcreate(arname);
   581  	}
   582  	Binit(&bar, fd, OREAD);
   583  	Bseek(&bar,seek(fd,0,1), 1);
   584  	/* leave note group behind when writing archive; i.e. sidestep interrupts */
   585  	rfork(RFNOTEG);
   586  	Bseek(&bar, 0, 2);
   587  	bp = newmember();
   588  	for(i=0; i<count && files[i]; i++) {
   589  		file = files[i];
   590  		files[i] = 0;
   591  		bfile = Bopen(file, OREAD);
   592  		if(!bfile) {
   593  			fprint(2, "pack: cannot open %s\n", file);
   594  			errors++;
   595  		} else {
   596  			mesg('q', file);
   597  			armove(bfile, 0, bp);
   598  			if (!arwrite(fd, bp))
   599  				wrerr();
   600  			free(bp->member);
   601  			bp->member = 0;
   602  			Bterm(bfile);
   603  		}
   604  	}
   605  	free(bp);
   606  	close(fd);
   607  }
   608  
   609  /*
   610   *	does the object header line p match the last one we saw?
   611   *	update *lastp if it gets more specific.
   612   */
   613  int
   614  matchhdr(char *p, char **lastp)
   615  {
   616  	int n;
   617  	char *last;
   618  	
   619  	// no information?
   620  	last = *lastp;
   621  	if(last == nil) {
   622  		*lastp = strdup(p);
   623  		return 1;
   624  	}
   625  
   626  	// identical match?
   627  	if(strcmp(last, p) == 0)
   628  		return 1;
   629  
   630  	// last has extra fields
   631  	n = strlen(p);
   632  	if(n < strlen(last) && last[n] == ' ')
   633  		return 1;
   634  
   635  	// p has extra fields - save in last
   636  	n = strlen(last);
   637  	if(n < strlen(p) && p[n] == ' ') {
   638  		free(last);
   639  		*lastp = strdup(p);
   640  		return 1;
   641  	}
   642  	
   643  	return 0;
   644  }	
   645  
   646  /*
   647   *	extract the symbol references from an object file
   648   */
   649  void
   650  scanobj(Biobuf *b, Arfile *ap, long size)
   651  {
   652  	int obj, goobject;
   653  	vlong offset, offset1;
   654  	Dir *d;
   655  	static int lastobj = -1;
   656  	uchar buf[4];
   657  	char *p;
   658  
   659  	if (!allobj)			/* non-object file encountered */
   660  		return;
   661  	offset = Boffset(b);
   662  	obj = objtype(b, 0);
   663  	if (obj < 0) {			/* not an object file */
   664  		/* maybe a foreign object file */
   665  		Bseek(b, offset, 0);
   666  		memset(buf, 0, sizeof buf);
   667  		Bread(b, buf, 4);
   668  		
   669  		/* maybe a foreign object file?  that's okay */
   670  		if((buf[0] == 0x7F && buf[1] == 'E' && buf[2] == 'L' && buf[3] == 'F') ||   // ELF
   671  		   (buf[0] == 0x4c && buf[1] == 0x01 || buf[0] == 0x64 && buf[1] == 0x86) || // Windows PE
   672  		   (buf[0] == 0xFE && buf[1] == 0xED && buf[2] == 0xFA && (buf[3]&~1) == 0xCE) ||  // Mach-O big-endian
   673  		   (buf[3] == 0xFE && buf[2] == 0xED && buf[1] == 0xFA && (buf[0]&~1) == 0xCE)) {  // Mach-O little-endian
   674  			Bseek(b, offset, 0);
   675  			return;
   676  		}
   677  		
   678  		if (!gflag || strcmp(file, pkgdef) != 0) {  /* don't clear allobj if it's pkg defs */
   679  			fprint(2, "pack: non-object file %s\n", file);
   680  			errors++;
   681  			allobj = 0;
   682  		}
   683  		d = dirfstat(Bfildes(b));
   684  		if (d != nil && d->length == 0) {
   685  			fprint(2, "pack: zero length file %s\n", file);
   686  			errors++;
   687  		}
   688  		free(d);
   689  		Bseek(b, offset, 0);
   690  		return;
   691  	}
   692  
   693  	goobject = 1;
   694  	offset1 = Boffset(b);
   695  	Bseek(b, offset, 0);
   696  	p = Brdstr(b, '\n', 1);
   697  	
   698  	// After the go object header comes the Go metadata,
   699  	// followed by ! on a line by itself.  If this is not a Go object,
   700  	// the ! comes immediately.  Catch that so we can avoid
   701  	// the call to scanpkg below, since scanpkg assumes that the
   702  	// Go metadata is present.
   703  	if(Bgetc(b) == '!')
   704  		goobject = 0;
   705  
   706  	Bseek(b, offset1, 0);
   707  	if(p == nil || strncmp(p, "go object ", 10) != 0) {
   708  		fprint(2, "pack: malformed object file %s\n", file);
   709  		errors++;
   710  		Bseek(b, offset, 0);
   711  		free(p);
   712  		return;
   713  	}
   714  	
   715  	if (!matchhdr(p, &objhdr)) {
   716  		fprint(2, "pack: inconsistent object file %s: [%s] vs [%s]\n", file, p, objhdr);
   717  		errors++;
   718  		allobj = 0;
   719  		free(p);
   720  		return;
   721  	}
   722  	free(p);
   723  
   724  	// Old check.  Should be impossible since objhdrs match, but keep the check anyway.
   725  	if (lastobj >= 0 && obj != lastobj) {
   726  		fprint(2, "pack: inconsistent object file %s\n", file);
   727  		errors++;
   728  		allobj = 0;
   729  		return;
   730  	}
   731  	lastobj = obj;
   732  		
   733  	if (!readar(b, obj, offset+size, 0)) {
   734  		fprint(2, "pack: invalid symbol reference in file %s\n", file);
   735  		errors++;
   736  		allobj = 0;
   737  		Bseek(b, offset, 0);
   738  		return;
   739  	}
   740  	Bseek(b, offset, 0);
   741  	objtraverse(objsym, ap);
   742  	if (gflag && goobject) {
   743  		scanpkg(b, size);
   744  		Bseek(b, offset, 0);
   745  	}
   746  }
   747  
   748  /*
   749   *	does line contain substring (length-limited)
   750   */
   751  int
   752  strstrn(char *line, int len, char *sub)
   753  {
   754  	int i;
   755  	int sublen;
   756  
   757  	sublen = strlen(sub);
   758  	for (i = 0; i < len - sublen; i++)
   759  		if (memcmp(line+i, sub, sublen) == 0)
   760  			return 1;
   761  	return 0;
   762  }
   763  
   764  /*
   765   *	package import data
   766   */
   767  int	safe = 1;
   768  char*	pkgname;
   769  char*	importblock;
   770  
   771  void
   772  getpkgdef(char **datap, int *lenp)
   773  {
   774  	char *tag, *hdr;
   775  
   776  	if(pkgname == nil) {
   777  		pkgname = "__emptyarchive__";
   778  		importblock = "";
   779  	}
   780  	
   781  	tag = "";
   782  	if(safe || Sflag)
   783  		tag = "safe";
   784  
   785  	hdr = "empty archive";
   786  	if(objhdr != nil)
   787  		hdr = objhdr;
   788  
   789  	*datap = smprint("%s\nimport\n$$\npackage %s %s\n%s\n$$\n", hdr, pkgname, tag, importblock);
   790  	*lenp = strlen(*datap);
   791  }
   792  
   793  /*
   794   *	extract the package definition data from an object file.
   795   *	there can be only one.
   796   */
   797  void
   798  scanpkg(Biobuf *b, long size)
   799  {
   800  	long n;
   801  	int c;
   802  	long start, end, pkgsize;
   803  	char *data, *line, pkgbuf[1024], *pkg;
   804  	int first;
   805  
   806  	/*
   807  	 * scan until $$
   808  	 */
   809  	for (n=0; n<size; ) {
   810  		c = Bgetc(b);
   811  		if(c == Beof)
   812  			break;
   813  		n++;
   814  		if(c != '$')
   815  			continue;
   816  		c = Bgetc(b);
   817  		if(c == Beof)
   818  			break;
   819  		n++;
   820  		if(c != '$')
   821  			continue;
   822  		goto foundstart;
   823  	}
   824  	// fprint(2, "pack: warning: no package import section in %s\n", file);
   825  	if(b != &bar || !pkgdefsafe)
   826  		safe = 0;	// non-Go file (C or assembly)
   827  	return;
   828  
   829  foundstart:
   830  	/* found $$; skip rest of line */
   831  	while((c = Bgetc(b)) != '\n')
   832  		if(c == Beof)
   833  			goto bad;
   834  
   835  	/* how big is it? */
   836  	first = 1;
   837  	start = end = 0;
   838  	for (n=0; n<size; n+=Blinelen(b)) {
   839  		line = Brdstr(b, '\n', 0);
   840  		if (line == nil)
   841  			goto bad;
   842  		if (first && strstrn(line, Blinelen(b), "package ")) {
   843  			if (Blinelen(b) > sizeof(pkgbuf)-1)
   844  				goto bad;
   845  			memmove(pkgbuf, line, Blinelen(b));
   846  			pkgbuf[Blinelen(b)] = '\0';
   847  			pkg = pkgbuf;
   848  			while(*pkg == ' ' || *pkg == '\t')
   849  				pkg++;
   850  			if(strncmp(pkg, "package ", 8) != 0)
   851  				goto bad;
   852  			pkg += 8;
   853  			data = pkg;
   854  			while(*pkg != ' ' && *pkg != '\n' && *pkg != '\0')
   855  				pkg++;
   856  			pkgname = armalloc(pkg - data + 1);
   857  			memmove(pkgname, data, pkg - data);
   858  			pkgname[pkg-data] = '\0';
   859  			if(strcmp(pkg, " safe\n") != 0 && (b != &bar || !pkgdefsafe))
   860  				safe = 0;
   861  			start = Boffset(b);  // after package statement
   862  			first = 0;
   863  			free(line);
   864  			continue;
   865  		}
   866  		if(line[0] == '$' && line[1] == '$') {
   867  			free(line);
   868  			goto foundend;
   869  		}
   870  		end = Boffset(b);  // before closing $$
   871  		free(line);
   872  	}
   873  bad:
   874  	fprint(2, "pack: bad package import section in %s\n", file);
   875  	errors++;
   876  	return;
   877  
   878  foundend:
   879  	if (start == 0)
   880  		return;
   881  	if (end == 0)
   882  		goto bad;
   883  	if(importblock != nil) {
   884  		fprint(2, "pack: multiple Go object files\n");
   885  		errors++;
   886  		return;
   887  	}
   888  	pkgsize = end-start;
   889  	data = armalloc(end - start + 1);
   890  	Bseek(b, start, 0);
   891  	if (Bread(b, data, pkgsize) != pkgsize) {
   892  		fprint(2, "pack: error reading package import section in %s\n", file);
   893  		errors++;
   894  		return;
   895  	}
   896  	data[end-start] = '\0';
   897  	importblock = data;
   898  }
   899  
   900  /*
   901   *	add text and data symbols to the symbol list
   902   */
   903  void
   904  objsym(Sym *s, void *p)
   905  {
   906  	int n;
   907  	Arsymref *as;
   908  	Arfile *ap;
   909  	char *ofile;
   910  
   911  	if (s->type != 'T' &&  s->type != 'D')
   912  		return;
   913  	ap = (Arfile*)p;
   914  	as = armalloc(sizeof(Arsymref));
   915  	as->offset = ap->size;
   916  	as->name = arstrdup(s->name);
   917  	as->file = arstrdup(file);
   918  	if(s->type == 'T' && duplicate(as->name, &ofile)) {
   919  		dupfound = 1;
   920  		fprint(2, "duplicate text symbol: %s and %s: %s\n", as->file, ofile, as->name);
   921  		errors++;
   922  		free(as->name);
   923  		free(as);
   924  		return;
   925  	}
   926  	as->type = s->type;
   927  	n = strlen(s->name);
   928  	symdefsize += 4+(n+1)+1;
   929  	as->len = n;
   930  	as->next = ap->sym;
   931  	ap->sym = as;
   932  }
   933  
   934  /*
   935   *	Check the symbol table for duplicate text symbols
   936   */
   937  int
   938  hashstr(char *name)
   939  {
   940  	int h;
   941  	char *cp;
   942  
   943  	h = 0;
   944  	for(cp = name; *cp; h += *cp++)
   945  		h *= 1119;
   946  	
   947  	// the code used to say
   948  	//	if(h < 0)
   949  	//		h = ~h;
   950  	// but on gcc 4.3 with -O2 on some systems,
   951  	// the if(h < 0) gets compiled away as not possible.
   952  	// use a mask instead, leaving plenty of bits but
   953  	// definitely not the sign bit.
   954  
   955  	return h & 0xfffffff;
   956  }
   957  
   958  int
   959  duplicate(char *name, char **ofile)
   960  {
   961  	Hashchain *p;
   962  	int h;
   963  
   964  	h = hashstr(name) % NHASH;
   965  
   966  	for(p = hash[h]; p; p = p->next)
   967  		if(strcmp(p->name, name) == 0) {
   968  			*ofile = p->file;
   969  			return 1;
   970  		}
   971  	p = armalloc(sizeof(Hashchain));
   972  	p->next = hash[h];
   973  	p->name = name;
   974  	p->file = file;
   975  	hash[h] = p;
   976  	*ofile = nil;
   977  	return 0;
   978  }
   979  
   980  /*
   981   *	open an archive and validate its header
   982   */
   983  int
   984  openar(char *arname, int mode, int errok)
   985  {
   986  	int fd;
   987  	char mbuf[SARMAG];
   988  
   989  	fd = open(arname, mode);
   990  	if(fd >= 0){
   991  		if(read(fd, mbuf, SARMAG) != SARMAG || strncmp(mbuf, ARMAG, SARMAG)) {
   992  			fprint(2, "pack: %s not in archive format\n", arname);
   993  			exits("error");
   994  		}
   995  	}else if(!errok){
   996  		fprint(2, "pack: cannot open %s: %r\n", arname);
   997  		exits("error");
   998  	}
   999  	return fd;
  1000  }
  1001  
  1002  /*
  1003   *	create an archive and set its header
  1004   */
  1005  int
  1006  arcreate(char *arname)
  1007  {
  1008  	int fd;
  1009  
  1010  	fd = create(arname, OWRITE, 0664);
  1011  	if(fd < 0){
  1012  		fprint(2, "pack: cannot create %s: %r\n", arname);
  1013  		exits("error");
  1014  	}
  1015  	if(write(fd, ARMAG, SARMAG) != SARMAG)
  1016  		wrerr();
  1017  	return fd;
  1018  }
  1019  
  1020  /*
  1021   *		error handling
  1022   */
  1023  void
  1024  wrerr(void)
  1025  {
  1026  	perror("pack: write error");
  1027  	exits("error");
  1028  }
  1029  
  1030  void
  1031  rderr(void)
  1032  {
  1033  	perror("pack: read error");
  1034  	exits("error");
  1035  }
  1036  
  1037  void
  1038  phaseerr(int offset)
  1039  {
  1040  	fprint(2, "pack: phase error at offset %d\n", offset);
  1041  	exits("error");
  1042  }
  1043  
  1044  void
  1045  usage(void)
  1046  {
  1047  	fprint(2, "usage: pack [%s][%s][P prefix] archive files ...\n", opt, man);
  1048  	exits("error");
  1049  }
  1050  
  1051  /*
  1052   *	read the header for the next archive member
  1053   */
  1054  Armember *
  1055  getdir(Biobuf *b)
  1056  {
  1057  	Armember *bp;
  1058  	char *cp;
  1059  	static char name[ARNAMESIZE+1];
  1060  
  1061  	bp = newmember();
  1062  	if(HEADER_IO(Bread, b, bp->hdr)) {
  1063  		free(bp);
  1064  		return 0;
  1065  	}
  1066  	if(strncmp(bp->hdr.fmag, ARFMAG, sizeof(bp->hdr.fmag)))
  1067  		phaseerr(Boffset(b));
  1068  	strncpy(name, bp->hdr.name, sizeof(bp->hdr.name));
  1069  	cp = name+sizeof(name)-1;
  1070  	while(*--cp==' ')
  1071  		;
  1072  	cp[1] = '\0';
  1073  	file = arstrdup(name);
  1074  	bp->date = strtol(bp->hdr.date, 0, 0);
  1075  	bp->size = strtol(bp->hdr.size, 0, 0);
  1076  	return bp;
  1077  }
  1078  
  1079  /*
  1080   *	Copy the file referenced by fd to the temp file
  1081   */
  1082  void
  1083  armove(Biobuf *b, Arfile *ap, Armember *bp)
  1084  {
  1085  	char *cp;
  1086  	Dir *d;
  1087  	vlong n;
  1088  
  1089  	d = dirfstat(Bfildes(b));
  1090  	if (d == nil) {
  1091  		fprint(2, "pack: cannot stat %s\n", file);
  1092  		return;
  1093  	}
  1094  
  1095  	trim(file, bp->hdr.name, sizeof(bp->hdr.name));
  1096  	for (cp = strchr(bp->hdr.name, 0);		/* blank pad on right */
  1097  		cp < bp->hdr.name+sizeof(bp->hdr.name); cp++)
  1098  			*cp = ' ';
  1099  	sprint(bp->hdr.date, "%-12ld", 0L);  // was d->mtime but removed for idempotent builds
  1100  	sprint(bp->hdr.uid, "%-6d", 0);
  1101  	sprint(bp->hdr.gid, "%-6d", 0);
  1102  	sprint(bp->hdr.mode, "%-8lo", d->mode);
  1103  	sprint(bp->hdr.size, "%-10lld", d->length);
  1104  	strncpy(bp->hdr.fmag, ARFMAG, 2);
  1105  	bp->size = d->length;
  1106  	arread(b, bp);
  1107  	n = bp->size;
  1108  	if (n&1)
  1109  		n++;
  1110  	if (ap) {
  1111  		arinsert(ap, bp);
  1112  		ap->size += n+SAR_HDR;
  1113  	}
  1114  	free(d);
  1115  }
  1116  
  1117  /*
  1118   *	Copy the archive member at the current offset into the temp file.
  1119   */
  1120  void
  1121  arcopy(Biobuf *b, Arfile *ap, Armember *bp)
  1122  {
  1123  	long n;
  1124  
  1125  	arread(b, bp);
  1126  	n = bp->size;
  1127  	if (n & 01)
  1128  		n++;
  1129  	if (ap) {
  1130  		arinsert(ap, bp);
  1131  		ap->size += n+SAR_HDR;
  1132  	}
  1133  }
  1134  
  1135  /*
  1136   *	Skip an archive member
  1137   */
  1138  void
  1139  skip(Biobuf *bp, vlong len)
  1140  {
  1141  	if (len & 01)
  1142  		len++;
  1143  	Bseek(bp, len, 1);
  1144  }
  1145  
  1146  void
  1147  checksafe(Biobuf *bp, vlong len)
  1148  {
  1149  	char *p;
  1150  	vlong end;
  1151  
  1152  	if (len & 01)
  1153  		len++;
  1154  	end = Boffset(bp) + len;
  1155  
  1156  	p = Brdline(bp, '\n');
  1157  	if(p == nil || strncmp(p, "go object ", 10) != 0)
  1158  		goto done;
  1159  	for(;;) {
  1160  		p = Brdline(bp, '\n');
  1161  		if(p == nil || Boffset(bp) >= end)
  1162  			goto done;
  1163  		if(strncmp(p, "$$\n", 3) == 0)
  1164  			break;
  1165  	}
  1166  	p = Brdline(bp, '\n');
  1167  	if(p == nil || Boffset(bp) > end)
  1168  		goto done;
  1169  	if(Blinelen(bp) > 8+6 && strncmp(p, "package ", 8) == 0 && strncmp(p+Blinelen(bp)-6, " safe\n", 6) == 0)
  1170  		pkgdefsafe = 1;
  1171  
  1172  done:
  1173  	Bseek(bp, end, 0);
  1174  }
  1175  
  1176  /*
  1177   *	Stream the three temp files to an archive
  1178   */
  1179  void
  1180  install(char *arname, Arfile *astart, Arfile *amiddle, Arfile *aend, int createflag)
  1181  {
  1182  	int fd;
  1183  
  1184  	if(allobj && dupfound) {
  1185  		fprint(2, "%s not changed\n", arname);
  1186  		return;
  1187  	}
  1188  	/* leave note group behind when copying back; i.e. sidestep interrupts */
  1189  	rfork(RFNOTEG);
  1190  
  1191  	if(createflag)
  1192  		fprint(2, "pack: creating %s\n", arname);
  1193  	fd = arcreate(arname);
  1194  
  1195  	if(allobj)
  1196  		rl(fd);
  1197  
  1198  	if (astart) {
  1199  		arstream(fd, astart);
  1200  		arfree(astart);
  1201  	}
  1202  	if (amiddle) {
  1203  		arstream(fd, amiddle);
  1204  		arfree(amiddle);
  1205  	}
  1206  	if (aend) {
  1207  		arstream(fd, aend);
  1208  		arfree(aend);
  1209  	}
  1210  	close(fd);
  1211  }
  1212  
  1213  void
  1214  rl(int fd)
  1215  {
  1216  	Biobuf b;
  1217  	char *cp;
  1218  	struct ar_hdr a;
  1219  	long len;
  1220  	int headlen;
  1221  	char *pkgdefdata;
  1222  	int pkgdefsize;
  1223  
  1224  	pkgdefdata = nil;
  1225  	pkgdefsize = 0;
  1226  
  1227  	Binit(&b, fd, OWRITE);
  1228  	Bseek(&b,seek(fd,0,1), 0);
  1229  
  1230  	len = symdefsize;
  1231  	if(len&01)
  1232  		len++;
  1233  	sprint(a.date, "%-12ld", 0L);  // time(0)
  1234  	sprint(a.uid, "%-6d", 0);
  1235  	sprint(a.gid, "%-6d", 0);
  1236  	sprint(a.mode, "%-8lo", 0644L);
  1237  	sprint(a.size, "%-10ld", len);
  1238  	strncpy(a.fmag, ARFMAG, 2);
  1239  	strcpy(a.name, symdef);
  1240  	for (cp = strchr(a.name, 0);		/* blank pad on right */
  1241  		cp < a.name+sizeof(a.name); cp++)
  1242  			*cp = ' ';
  1243  	if(HEADER_IO(Bwrite, &b, a))
  1244  			wrerr();
  1245  
  1246  	headlen = Boffset(&b);
  1247  	len += headlen;
  1248  	if (gflag) {
  1249  		getpkgdef(&pkgdefdata, &pkgdefsize);
  1250  		len += SAR_HDR + pkgdefsize;
  1251  		if (len & 1)
  1252  			len++;
  1253  	}
  1254  	if (astart) {
  1255  		wrsym(&b, len, astart->sym);
  1256  		len += astart->size;
  1257  	}
  1258  	if(amiddle) {
  1259  		wrsym(&b, len, amiddle->sym);
  1260  		len += amiddle->size;
  1261  	}
  1262  	if(aend)
  1263  		wrsym(&b, len, aend->sym);
  1264  
  1265  	if(symdefsize&0x01)
  1266  		Bputc(&b, 0);
  1267  
  1268  	if (gflag) {
  1269  		len = pkgdefsize;
  1270  		sprint(a.date, "%-12ld", 0L);  // time(0)
  1271  		sprint(a.uid, "%-6d", 0);
  1272  		sprint(a.gid, "%-6d", 0);
  1273  		sprint(a.mode, "%-8lo", 0644L);
  1274  		sprint(a.size, "%-10ld", (len + 1) & ~1);
  1275  		strncpy(a.fmag, ARFMAG, 2);
  1276  		strcpy(a.name, pkgdef);
  1277  		for (cp = strchr(a.name, 0);		/* blank pad on right */
  1278  			cp < a.name+sizeof(a.name); cp++)
  1279  				*cp = ' ';
  1280  		if(HEADER_IO(Bwrite, &b, a))
  1281  				wrerr();
  1282  
  1283  		if (Bwrite(&b, pkgdefdata, pkgdefsize) != pkgdefsize)
  1284  			wrerr();
  1285  		if(len&0x01)
  1286  			Bputc(&b, 0);
  1287  	}
  1288  	Bterm(&b);
  1289  }
  1290  
  1291  /*
  1292   *	Write the defined symbols to the symdef file
  1293   */
  1294  void
  1295  wrsym(Biobuf *bp, long offset, Arsymref *as)
  1296  {
  1297  	int off;
  1298  
  1299  	while(as) {
  1300  		Bputc(bp, as->type);
  1301  		off = as->offset+offset;
  1302  		Bputc(bp, off);
  1303  		Bputc(bp, off>>8);
  1304  		Bputc(bp, off>>16);
  1305  		Bputc(bp, off>>24);
  1306  		if (Bwrite(bp, as->name, as->len+1) != as->len+1)
  1307  			wrerr();
  1308  		as = as->next;
  1309  	}
  1310  }
  1311  
  1312  /*
  1313   *	Check if the archive member matches an entry on the command line.
  1314   */
  1315  int
  1316  match(int count, char **files)
  1317  {
  1318  	int i;
  1319  	char name[ARNAMESIZE+1];
  1320  
  1321  	for(i=0; i<count; i++) {
  1322  		if(files[i] == 0)
  1323  			continue;
  1324  		trim(files[i], name, ARNAMESIZE);
  1325  		if(strncmp(name, file, ARNAMESIZE) == 0) {
  1326  			file = files[i];
  1327  			files[i] = 0;
  1328  			return 1;
  1329  		}
  1330  	}
  1331  	return 0;
  1332  }
  1333  
  1334  /*
  1335   *	compare the current member to the name of the pivot member
  1336   */
  1337  int
  1338  bamatch(char *file, char *pivot)
  1339  {
  1340  	static int state = 0;
  1341  
  1342  	switch(state)
  1343  	{
  1344  	case 0:			/* looking for position file */
  1345  		if (aflag) {
  1346  			if (strncmp(file, pivot, ARNAMESIZE) == 0)
  1347  				state = 1;
  1348  		} else if (bflag) {
  1349  			if (strncmp(file, pivot, ARNAMESIZE) == 0) {
  1350  				state = 2;	/* found */
  1351  				return 1;
  1352  			}
  1353  		}
  1354  		break;
  1355  	case 1:			/* found - after previous file */
  1356  		state = 2;
  1357  		return 1;
  1358  	case 2:			/* already found position file */
  1359  		break;
  1360  	}
  1361  	return 0;
  1362  }
  1363  
  1364  /*
  1365   *	output a message, if 'v' option was specified
  1366   */
  1367  void
  1368  mesg(int c, char *file)
  1369  {
  1370  
  1371  	if(vflag)
  1372  		Bprint(&bout, "%c - %s\n", c, file);
  1373  }
  1374  
  1375  /*
  1376   *	isolate file name by stripping leading directories and trailing slashes
  1377   */
  1378  void
  1379  trim(char *s, char *buf, int n)
  1380  {
  1381  	char *p, *q;
  1382  
  1383  	for(;;) {
  1384  		p = strrchr(s, '/');
  1385  		q = strrchr(s, '\\');
  1386  		if (q > p)
  1387  			p = q;
  1388  		if (!p) {		/* no (back)slash in name */
  1389  			strncpy(buf, s, n);
  1390  			return;
  1391  		}
  1392  		if (p[1] != 0) {	/* p+1 is first char of file name */
  1393  			strncpy(buf, p+1, n);
  1394  			return;
  1395  		}
  1396  		*p = 0;			/* strip trailing (back)slash */
  1397  	}
  1398  }
  1399  
  1400  /*
  1401   *	utilities for printing long form of 't' command
  1402   */
  1403  #define	SUID	04000
  1404  #define	SGID	02000
  1405  #define	ROWN	0400
  1406  #define	WOWN	0200
  1407  #define	XOWN	0100
  1408  #define	RGRP	040
  1409  #define	WGRP	020
  1410  #define	XGRP	010
  1411  #define	ROTH	04
  1412  #define	WOTH	02
  1413  #define	XOTH	01
  1414  #define	STXT	01000
  1415  
  1416  void
  1417  longt(Armember *bp)
  1418  {
  1419  	char *cp;
  1420  
  1421  	pmode(strtoul(bp->hdr.mode, 0, 8));
  1422  	Bprint(&bout, "%3ld/%1ld", strtol(bp->hdr.uid, 0, 0), strtol(bp->hdr.gid, 0, 0));
  1423  	Bprint(&bout, "%7ld", bp->size);
  1424  	cp = ctime(bp->date);
  1425  	Bprint(&bout, " %-12.12s %-4.4s ", cp+4, cp+24);
  1426  }
  1427  
  1428  int	m1[] = { 1, ROWN, 'r', '-' };
  1429  int	m2[] = { 1, WOWN, 'w', '-' };
  1430  int	m3[] = { 2, SUID, 's', XOWN, 'x', '-' };
  1431  int	m4[] = { 1, RGRP, 'r', '-' };
  1432  int	m5[] = { 1, WGRP, 'w', '-' };
  1433  int	m6[] = { 2, SGID, 's', XGRP, 'x', '-' };
  1434  int	m7[] = { 1, ROTH, 'r', '-' };
  1435  int	m8[] = { 1, WOTH, 'w', '-' };
  1436  int	m9[] = { 2, STXT, 't', XOTH, 'x', '-' };
  1437  
  1438  int	*m[] = { m1, m2, m3, m4, m5, m6, m7, m8, m9};
  1439  
  1440  void
  1441  pmode(long mode)
  1442  {
  1443  	int **mp;
  1444  
  1445  	for(mp = &m[0]; mp < &m[9];)
  1446  		select(*mp++, mode);
  1447  }
  1448  
  1449  void
  1450  select(int *ap, long mode)
  1451  {
  1452  	int n;
  1453  
  1454  	n = *ap++;
  1455  	while(--n>=0 && (mode&*ap++)==0)
  1456  		ap++;
  1457  	Bputc(&bout, *ap);
  1458  }
  1459  
  1460  /*
  1461   *	Temp file I/O subsystem.  We attempt to cache all three temp files in
  1462   *	core.  When we run out of memory we spill to disk.
  1463   *	The I/O model assumes that temp files:
  1464   *		1) are only written on the end
  1465   *		2) are only read from the beginning
  1466   *		3) are only read after all writing is complete.
  1467   *	The architecture uses one control block per temp file.  Each control
  1468   *	block anchors a chain of buffers, each containing an archive member.
  1469   */
  1470  Arfile *
  1471  newtempfile(char *name)		/* allocate a file control block */
  1472  {
  1473  	Arfile *ap;
  1474  
  1475  	ap = armalloc(sizeof(Arfile));
  1476  	ap->fname = name;
  1477  	return ap;
  1478  }
  1479  
  1480  Armember *
  1481  newmember(void)			/* allocate a member buffer */
  1482  {
  1483  	return armalloc(sizeof(Armember));
  1484  }
  1485  
  1486  void
  1487  arread(Biobuf *b, Armember *bp)	/* read an image into a member buffer */
  1488  {
  1489  	int i;
  1490  	vlong off;
  1491  
  1492  	bp->member = armalloc(bp->size);
  1493  	
  1494  	// If P flag is set, let arread_cutprefix try.
  1495  	// If it succeeds, we're done.  If not, fall back
  1496  	// to a direct copy.
  1497  	off = Boffset(b);
  1498  	if(Pflag && arread_cutprefix(b, bp))
  1499  		return;
  1500  	Bseek(b, off, 0);
  1501  
  1502  	i = Bread(b, bp->member, bp->size);
  1503  	if (i < 0) {
  1504  		free(bp->member);
  1505  		bp->member = 0;
  1506  		rderr();
  1507  	}
  1508  	if(bp->size&1)
  1509  		Bgetc(b);
  1510  }
  1511  
  1512  /*
  1513   * insert a member buffer into the member chain
  1514   */
  1515  void
  1516  arinsert(Arfile *ap, Armember *bp)
  1517  {
  1518  	bp->next = 0;
  1519  	if (!ap->tail)
  1520  		ap->head = bp;
  1521  	else
  1522  		ap->tail->next = bp;
  1523  	ap->tail = bp;
  1524  }
  1525  
  1526  /*
  1527   *	stream the members in a temp file to the file referenced by 'fd'.
  1528   */
  1529  void
  1530  arstream(int fd, Arfile *ap)
  1531  {
  1532  	Armember *bp;
  1533  
  1534  	/* dump the in-core buffers */
  1535  	for (bp = ap->head; bp; bp = bp->next) {
  1536  		if (!arwrite(fd, bp))
  1537  			wrerr();
  1538  	}
  1539  }
  1540  
  1541  /*
  1542   *	write a member to 'fd'.
  1543   */
  1544  int
  1545  arwrite(int fd, Armember *bp)
  1546  {
  1547  	int len;
  1548  
  1549  	if(HEADER_IO(write, fd, bp->hdr))
  1550  		return 0;
  1551  	len = bp->size;
  1552  	if (len & 01)
  1553  		len++;
  1554  	if (write(fd, bp->member, len) != len)
  1555  		return 0;
  1556  	return 1;
  1557  }
  1558  
  1559  void
  1560  arfree(Arfile *ap)		/* free a member buffer */
  1561  {
  1562  	Armember *bp, *next;
  1563  
  1564  	for (bp = ap->head; bp; bp = next) {
  1565  		next = bp->next;
  1566  		if (bp->member)
  1567  			free(bp->member);
  1568  		free(bp);
  1569  	}
  1570  	free(ap);
  1571  }
  1572  
  1573  /*
  1574   *	allocate space for a control block or member buffer.  if the malloc
  1575   *	fails we try to reclaim space by spilling previously allocated
  1576   *	member buffers.
  1577   */
  1578  void *
  1579  armalloc(int n)
  1580  {
  1581  	char *cp;
  1582  
  1583  	// bump so that arwrite can do the same
  1584  	if(n&1)
  1585  		n++;
  1586  
  1587  	cp = malloc(n);
  1588  	if (cp) {
  1589  		memset(cp, 0, n);
  1590  		return cp;
  1591  	}
  1592  	fprint(2, "pack: out of memory\n");
  1593  	exits("malloc");
  1594  	return 0;
  1595  }
  1596  
  1597  char *
  1598  arstrdup(char *s)
  1599  {
  1600  	char *t;
  1601  
  1602  	t = armalloc(strlen(s) + 1);
  1603  	strcpy(t, s);
  1604  	return t;
  1605  }
  1606  
  1607  
  1608  /*
  1609   *	Parts of libmach we're not supposed
  1610   *	to look at but need for arread_cutprefix.
  1611   */
  1612  extern int _read5(Biobuf*, Prog*);
  1613  extern int _read6(Biobuf*, Prog*);
  1614  extern int _read8(Biobuf*, Prog*);
  1615  int (*reader[256])(Biobuf*, Prog*) = {
  1616  	[ObjArm] = _read5,
  1617  	[ObjAmd64] = _read6,
  1618  	[Obj386] = _read8,
  1619  };
  1620  
  1621  /*
  1622   *	copy b into bp->member but rewrite object
  1623   *	during copy to drop prefix from all file names.
  1624   *	return 1 if b was recognized as an object file
  1625   *	and copied successfully, 0 otherwise.
  1626   */
  1627  int
  1628  arread_cutprefix(Biobuf *b, Armember *bp)
  1629  {
  1630  	vlong offset, o, end;
  1631  	int n, t;
  1632  	int (*rd)(Biobuf*, Prog*);
  1633  	char *w, *inprefix;
  1634  	Prog p;
  1635  	
  1636  	offset = Boffset(b);
  1637  	end = offset + bp->size;
  1638  	t = objtype(b, nil);
  1639  	if(t < 0)
  1640  		return 0;
  1641  	if((rd = reader[t]) == nil)
  1642  		return 0;
  1643  	
  1644  	// copy header
  1645  	w = bp->member;
  1646  	n = Boffset(b) - offset;
  1647  	Bseek(b, -n, 1);
  1648  	if(Bread(b, w, n) != n)
  1649  		return 0;
  1650  	offset += n;
  1651  	w += n;
  1652  	
  1653  	// read object file one pseudo-instruction at a time,
  1654  	// eliding the file name instructions that refer to
  1655  	// the prefix.
  1656  	memset(&p, 0, sizeof p);
  1657  	inprefix = nil;
  1658  	while(Boffset(b) < end && rd(b, &p)) {
  1659  		if(p.kind == aName && p.type == UNKNOWN && p.sym == 1 && p.id[0] == '<') {
  1660  			// part of a file path.
  1661  			// we'll keep continuing (skipping the copy)
  1662  			// around the loop until either we get to a
  1663  			// name piece that should be kept or we see
  1664  			// the whole prefix.
  1665  
  1666  			if(inprefix == nil && prefix[0] == '/' && p.id[1] == '/' && p.id[2] == '\0') {
  1667  				// leading /
  1668  				inprefix = prefix+1;
  1669  			} else if(inprefix != nil) {
  1670  				// handle subsequent elements
  1671  				n = strlen(p.id+1);
  1672  				if(strncmp(p.id+1, inprefix, n) == 0 && (inprefix[n] == '/' || inprefix[n] == '\0')) {
  1673  					inprefix += n;
  1674  					if(inprefix[0] == '/')
  1675  						inprefix++;
  1676  				}
  1677  			}
  1678  			
  1679  			if(inprefix && inprefix[0] == '\0') {
  1680  				// reached end of prefix.
  1681  				// if we another path element follows,
  1682  				// nudge the offset to skip over the prefix we saw.
  1683  				// if not, leave offset alone, to emit the whole name.
  1684  				// additional name elements will not be skipped
  1685  				// because inprefix is now nil and we won't see another
  1686  				// leading / in this name.
  1687  				inprefix = nil;
  1688  				o = Boffset(b);
  1689  				if(o < end && rd(b, &p) && p.kind == aName && p.type == UNKNOWN && p.sym == 1 && p.id[0] == '<') {
  1690  					// print("skip %lld-%lld\n", offset, o);
  1691  					offset = o;
  1692  				}
  1693  			}
  1694  		} else {
  1695  			// didn't find the whole prefix.
  1696  			// give up and let it emit the entire name.
  1697  			inprefix = nil;
  1698  		}
  1699  
  1700  		// copy instructions
  1701  		if(!inprefix) {
  1702  			n = Boffset(b) - offset;
  1703  			Bseek(b, -n, 1);
  1704  			if(Bread(b, w, n) != n)
  1705  				return 0;
  1706  			offset += n;
  1707  			w += n;
  1708  		}
  1709  	}
  1710  	bp->size = w - (char*)bp->member;
  1711  	sprint(bp->hdr.size, "%-10lld", (vlong)bp->size);
  1712  	strncpy(bp->hdr.fmag, ARFMAG, 2);
  1713  	Bseek(b, end, 0);
  1714  	if(Boffset(b)&1)
  1715  		Bgetc(b);
  1716  	return 1;
  1717  }