Import ar(1) front-end. (aka 'BSD' ar)
Reviewed by: jkoshy Approved by: jkoshy (mentor) Tested by: erwin (ports build test on pointyhat) Sponsored by: Google Summer of Code 2007 Reviewed by (earlier version): Jaakko Heinonen <jh[AT]saunalahti.fi> Tested by (earlier version): Steve Kargl <sgk[AT]troutmask.apl.washington.edu> Tested by (earlier version): Martin Voros <martin_voros[AT]yahoo.com> Tested by (earlier version): swell.k[AT]gmail.com Tested by (earlier version): joel Tested by (earlier version): Alexey Shuvaev <shuvaev[AT]physik.uni-wuerzburg.de> Tested by (earlier version): Arjan van Leeuwen <avleeuwen[AT]gmail.com> Thanks to gabor@ for building ports for it. Thanks to erwin@ and kris@ for scheduling the ports build test on pointyhat. And thanks to many others for their feedback.
This commit is contained in:
parent
afb9fa5a4b
commit
bd49e931f2
12
usr.bin/ar/Makefile
Normal file
12
usr.bin/ar/Makefile
Normal file
@ -0,0 +1,12 @@
|
||||
# $FreeBSD$
|
||||
|
||||
PROG= ar
|
||||
VERSION= 1.0.2
|
||||
SRCS= ar.c read.c util.c write.c
|
||||
WARNS?= 5
|
||||
DPADD= ${LIBARCHIVE} ${LIBELF}
|
||||
LDADD= -larchive -lelf
|
||||
CFLAGS+= -DBSDAR_VERSION=\"${VERSION}\"
|
||||
LINKS= ${BINDIR}/ar ${BINDIR}/ranlib
|
||||
|
||||
.include <bsd.prog.mk>
|
407
usr.bin/ar/ar.1
Normal file
407
usr.bin/ar/ar.1
Normal file
@ -0,0 +1,407 @@
|
||||
.\" Copyright (c) 2007 Joseph Koshy. All rights reserved.
|
||||
.\"
|
||||
.\" Redistribution and use in source and binary forms, with or without
|
||||
.\" modification, are permitted provided that the following conditions
|
||||
.\" are met:
|
||||
.\" 1. Redistributions of source code must retain the above copyright
|
||||
.\" notice, this list of conditions and the following disclaimer.
|
||||
.\" 2. Redistributions in binary form must reproduce the above copyright
|
||||
.\" notice, this list of conditions and the following disclaimer in the
|
||||
.\" documentation and/or other materials provided with the distribution.
|
||||
.\"
|
||||
.\" This software is provided by Joseph Koshy ``as is'' and
|
||||
.\" any express or implied warranties, including, but not limited to, the
|
||||
.\" implied warranties of merchantability and fitness for a particular purpose
|
||||
.\" are disclaimed. in no event shall Joseph Koshy be liable
|
||||
.\" for any direct, indirect, incidental, special, exemplary, or consequential
|
||||
.\" damages (including, but not limited to, procurement of substitute goods
|
||||
.\" or services; loss of use, data, or profits; or business interruption)
|
||||
.\" however caused and on any theory of liability, whether in contract, strict
|
||||
.\" liability, or tort (including negligence or otherwise) arising in any way
|
||||
.\" out of the use of this software, even if advised of the possibility of
|
||||
.\" such damage.
|
||||
.\"
|
||||
.\" $FreeBSD$
|
||||
.\"
|
||||
.Dd August 31, 2007
|
||||
.Os
|
||||
.Dr AR 1
|
||||
.Dt RANLIB 1
|
||||
.Sh NAME
|
||||
.Nm ar ,
|
||||
.Nm ranlib
|
||||
.Nd manage archives
|
||||
.Sh SYNOPSIS
|
||||
.Nm
|
||||
.Fl d
|
||||
.Op Fl T
|
||||
.Op Fl j
|
||||
.Op Fl v
|
||||
.Op Fl z
|
||||
.Ar archive
|
||||
.Ar files ...
|
||||
.Nm
|
||||
.Fl m
|
||||
.Op Fl T
|
||||
.Op Fl a Ar position-after
|
||||
.Op Fl b Ar position-before
|
||||
.Op Fl i Ar position-before
|
||||
.Op Fl j
|
||||
.Op Fl s
|
||||
.Op Fl z
|
||||
.Ar archive
|
||||
.Ar files ...
|
||||
.Nm
|
||||
.Fl p
|
||||
.Op Fl T
|
||||
.Op Fl v
|
||||
.Ar archive
|
||||
.Op Ar files ...
|
||||
.Nm
|
||||
.Fl r
|
||||
.Op Fl T
|
||||
.Op Fl a Ar position-after
|
||||
.Op Fl b Ar position-before
|
||||
.Op Fl c
|
||||
.Op Fl i Ar position-before
|
||||
.Op Fl j
|
||||
.Op Fl s
|
||||
.Op Fl u
|
||||
.Op Fl v
|
||||
.Op Fl z
|
||||
.Ar archive
|
||||
.Ar files ...
|
||||
.Nm
|
||||
.Fl s
|
||||
.Op Fl j
|
||||
.Op Fl z
|
||||
.Ar archive
|
||||
.Nm
|
||||
.Fl t
|
||||
.Op Fl T
|
||||
.Op Fl v
|
||||
.Ar archive
|
||||
.Op Ar files ...
|
||||
.Nm
|
||||
.Fl x
|
||||
.Op Fl C
|
||||
.Op Fl T
|
||||
.Op Fl o
|
||||
.Op Fl u
|
||||
.Op Fl v
|
||||
.Ar archive
|
||||
.Op Ar files ...
|
||||
.Nm ranlib
|
||||
.Ar archive ...
|
||||
.Sh DESCRIPTION
|
||||
The
|
||||
.Nm
|
||||
utility creates and maintains groups of files combined into an
|
||||
archive.
|
||||
Once an archive has been created, new files can be added to it, and
|
||||
existing files can be extracted, deleted or replaced.
|
||||
.Pp
|
||||
Files are named in the archive by their last file name component,
|
||||
so if a file referenced by a path containing a
|
||||
.Dq /
|
||||
is archived, it will be named by the last component of the path.
|
||||
Similarly when matching paths listed on the command line against
|
||||
file names stored in the archive, only the last component of the
|
||||
path will be compared.
|
||||
.Pp
|
||||
The normal use of
|
||||
.Nm
|
||||
is for the creation and maintenance of libraries suitable for use
|
||||
with the link editor
|
||||
.Xr ld 1 ,
|
||||
although it is not restricted to this purpose.
|
||||
The
|
||||
.Nm
|
||||
utility can create and manage an archive symbol table (see
|
||||
.Xr ar 5 )
|
||||
used to speed up link editing operations.
|
||||
If a symbol table is present in an archive, it will be
|
||||
kept upto-date by subsequent operations on the archive (excepting
|
||||
the quick update specified by the
|
||||
.Fl q
|
||||
option).
|
||||
.Pp
|
||||
The
|
||||
.Nm ranlib
|
||||
utility is used to add an archive symbol table
|
||||
to an existing archive.
|
||||
.Sh OPTIONS
|
||||
The
|
||||
.Nm
|
||||
utility supports the following options:
|
||||
.Bl -tag -width indent
|
||||
.It Fl a Ar member-after
|
||||
When used with option
|
||||
.Fl m
|
||||
this option specifies that the archive members specified by
|
||||
arguments
|
||||
.Ar files ...
|
||||
are moved to after the archive member named by argument
|
||||
.Ar member-after .
|
||||
When used with option
|
||||
.Fl r
|
||||
this option specifies that the files specified by arguments
|
||||
.Ar files ...
|
||||
are added after the archive member named by argument
|
||||
.Ar member-after .
|
||||
.It Fl b Ar member-before
|
||||
When used with option
|
||||
.Fl m
|
||||
this option specifies that the archive members specified by
|
||||
arguments
|
||||
.Ar files ...
|
||||
are moved to before the archive member named by argument
|
||||
.Ar member-before .
|
||||
When used with option
|
||||
.Fl r
|
||||
this option specifies that the files specified by arguments
|
||||
.Ar files ...
|
||||
are added before the archive member named by argument
|
||||
.Ar member-before .
|
||||
.It Fl c
|
||||
Suppress the informational message printed when a new archive is
|
||||
created using the
|
||||
.Fl r
|
||||
and
|
||||
.Fl q
|
||||
options.
|
||||
.It Fl C
|
||||
Prevent extracted files from replacing like-named files
|
||||
in the file system.
|
||||
.It Fl d
|
||||
Delete the members named by arguments
|
||||
.Ar files ...
|
||||
from the archive specified by argument
|
||||
.Ar archive .
|
||||
The archive's symbol table, if present, is updated to reflect
|
||||
the new contents of the archive.
|
||||
.It Fl f
|
||||
Synonymous with option
|
||||
.Fl T .
|
||||
.It Fl i Ar member-before
|
||||
Synonymous with option
|
||||
.Fl b .
|
||||
.It Fl j
|
||||
Compress the resulting archive with
|
||||
.Xr bzip2 1 .
|
||||
.It Fl m
|
||||
Move archive members specified by arguments
|
||||
.Ar files ...
|
||||
within the archive.
|
||||
If a position has been specified by one of the
|
||||
.Fl a ,
|
||||
.Fl b
|
||||
or
|
||||
.Fl i
|
||||
options, the members are moved to before or after the specified
|
||||
position.
|
||||
If no position has been specified, the specified members are moved
|
||||
to the end of the archive.
|
||||
If the archive has a symbol table, it is updated to reflect the
|
||||
new contents of the archive.
|
||||
.It Fl o
|
||||
Preserve the original modification times of members when extracting
|
||||
them.
|
||||
.It Fl p
|
||||
Write the contents of the specified archive members named by
|
||||
arguments
|
||||
.Ar files ...
|
||||
to standard output.
|
||||
If no members were specified, the contents of all the files in the
|
||||
archive are written in the order they appear in the archive.
|
||||
.It Fl q
|
||||
Append the files specified by arguments
|
||||
.Ar files ...
|
||||
to the archive specified by argument
|
||||
.Ar archive
|
||||
without checking if the files already exist in the archive and
|
||||
without updating the archive's symbol table.
|
||||
If the archive file
|
||||
.Ar archive
|
||||
does not already exist, a new archive is created.
|
||||
However, to be compatible with GNU
|
||||
.Nm ,
|
||||
.Fl q
|
||||
is implemented as a synonym for
|
||||
.Fl r .
|
||||
.It Fl r
|
||||
Replace (add) the files specified by arguments
|
||||
.Ar files ...
|
||||
in the archive specified by argument
|
||||
.Ar archive ,
|
||||
creating the archive if necessary.
|
||||
Files that replace existing files do not change the order of files
|
||||
within the archive.
|
||||
If a file named in arguments
|
||||
.Ar files ...
|
||||
does not exist, existing members in the archive that match that
|
||||
name are not changed.
|
||||
New files are added to the end of the archive unless one of the
|
||||
positioning options
|
||||
.Fl a ,
|
||||
.Fl b
|
||||
or
|
||||
.Fl i
|
||||
is specified.
|
||||
The archive symbol table, if it exists, is updated to reflect the
|
||||
new state of the archive.
|
||||
.It Fl s
|
||||
Add an archive symbol table (see
|
||||
.Xr ar 5 )
|
||||
to the archive specified by argument
|
||||
.Ar archive .
|
||||
Invoking
|
||||
.Nm
|
||||
with the
|
||||
.Fl s
|
||||
option alone is equivalent to invoking
|
||||
.Nm ranlib .
|
||||
.It Fl t
|
||||
List the files specified by arguments
|
||||
.Ar files ...
|
||||
in the order in which they appear in the archive, one per line.
|
||||
If no files are specified, all files in the archive are listed.
|
||||
.It Fl T
|
||||
Use only the first fifteen characters of the archive member name or
|
||||
command line file name argument when naming archive members.
|
||||
.It Fl u
|
||||
Conditionally update the archive or extract members.
|
||||
When used with the
|
||||
.Fl r
|
||||
option, files named by arguments
|
||||
.Ar files ...
|
||||
will be replaced in the archive if they are newer than their
|
||||
archived versions.
|
||||
When used with the
|
||||
.Fl x
|
||||
option, the members specified by arguments
|
||||
.Ar files ...
|
||||
will be extracted only if they are newer than the corresponding
|
||||
files in the file system.
|
||||
.It Fl v
|
||||
Provide verbose output.
|
||||
When used with the
|
||||
.Fl d ,
|
||||
.Fl m ,
|
||||
.Fl q
|
||||
or
|
||||
.Fl x
|
||||
options,
|
||||
.Nm
|
||||
gives a file-by-file description of the archive modification being
|
||||
performed, which consists of three white-space seperated fields:
|
||||
the option letter, a dash
|
||||
.Dq "-" ,
|
||||
and the file name.
|
||||
When used with the
|
||||
.Fl r
|
||||
option,
|
||||
.Nm
|
||||
displays the description as above, but the initial letter is an
|
||||
.Dq a
|
||||
if the file is added to the archive, or an
|
||||
.Dr r
|
||||
if the file replaces a file already in the archive.
|
||||
When used with the
|
||||
.Fl p
|
||||
option, the name of the file enclosed in
|
||||
.Dq <
|
||||
and
|
||||
.Dq >
|
||||
characters is written to standard output preceded by a single newline
|
||||
character and followed by two newline characters.
|
||||
The contents of the named file follow the file name.
|
||||
When used with the
|
||||
.Fl t
|
||||
option,
|
||||
.Nm
|
||||
displays eight whitespace separated fields:
|
||||
the file permissions as displayed by
|
||||
.Xr strmode 3 ,
|
||||
decimal user and group IDs separated by a slash (
|
||||
.Dq / Ns ) ,
|
||||
the file size in bytes, the file modification time in
|
||||
.Xr strftime 3
|
||||
format
|
||||
.Dq "%b %e %H:%M %Y" ,
|
||||
and the name of the file.
|
||||
.It Fl x
|
||||
Extract archive members specified by arguments
|
||||
.Ar files ...
|
||||
into the current directory.
|
||||
If no members have been specified, extract all members of the archive.
|
||||
If the file corresponding to an extracted member does not exist it
|
||||
will be created.
|
||||
If the file corresponding to an extracted member does exist, its owner
|
||||
and group will not be changed while its contents will be overwritten
|
||||
and its permissions will set to that entered in the archive.
|
||||
The file's access and modification time would be that of the time
|
||||
of extraction unless the
|
||||
.Fl o
|
||||
option was specified.
|
||||
.It Fl z
|
||||
Compress the resulting archive with
|
||||
.Xr gzip 1 .
|
||||
.El
|
||||
.Sh EXAMPLES
|
||||
To create a new archive
|
||||
.Pa ex.a
|
||||
containing three files
|
||||
.Pa ex1.o ,
|
||||
.Pa ex2.o
|
||||
and
|
||||
.Pa ex3.o ,
|
||||
use:
|
||||
.Dl "ar -rc ex.a ex1.o ex2.o ex3.o"
|
||||
.Pp
|
||||
To add an archive symbol table to an existing archive
|
||||
.Pa ex.a ,
|
||||
use:
|
||||
.Dl "ar -s ex.a"
|
||||
.Pp
|
||||
To delete file
|
||||
.Pa ex1.o
|
||||
from archive
|
||||
.Pa ex.a ,
|
||||
use:
|
||||
.D1 "ar -d ex.a ex1.o"
|
||||
.Pp
|
||||
To verbosely list the contents of archive
|
||||
.Pa ex.a ,
|
||||
use:
|
||||
.D1 "ar -tv ex.a"
|
||||
.Sh DIAGNOSTICS
|
||||
.Ex -std
|
||||
.Sh SEE ALSO
|
||||
.Xr ld 1 ,
|
||||
.Xr archive 3 ,
|
||||
.Xr elf 3 ,
|
||||
.Xr strftime 3 ,
|
||||
.Xr strmode 3 ,
|
||||
.Xr ar 5
|
||||
.\" .Sh COMPATIBILITY
|
||||
.\" .Nm
|
||||
.\" is expected to be compatible with GNU and SVR4
|
||||
.\" .Nm .
|
||||
.\" .Sh STANDARDS
|
||||
.\" Do the POSIX/SuSv3 standards have anything to say about AR(1)?
|
||||
.Sh HISTORY
|
||||
An
|
||||
.Nm
|
||||
command first appeared in AT&T UNIX Version 1.
|
||||
In
|
||||
.Fx 8 ,
|
||||
.An "Kai Wang" Aq kaiw@FreeBSD.org
|
||||
reimplemented
|
||||
.Nm ar
|
||||
and
|
||||
.Nm ranlib
|
||||
using the
|
||||
.Lb libarchive
|
||||
and the
|
||||
.Lb libelf .
|
377
usr.bin/ar/ar.c
Normal file
377
usr.bin/ar/ar.c
Normal file
@ -0,0 +1,377 @@
|
||||
/*-
|
||||
* Copyright (c) 2007 Kai Wang
|
||||
* Copyright (c) 2007 Tim Kientzle
|
||||
* Copyright (c) 2007 Joseph Koshy
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer
|
||||
* in this position and unchanged.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*-
|
||||
* Copyright (c) 1990, 1993, 1994
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to Berkeley by
|
||||
* Hugh Smith at The University of Guelph.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/queue.h>
|
||||
#include <sys/types.h>
|
||||
#include <archive.h>
|
||||
#include <errno.h>
|
||||
#include <getopt.h>
|
||||
#include <libgen.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sysexits.h>
|
||||
|
||||
#include "ar.h"
|
||||
|
||||
enum options
|
||||
{
|
||||
OPTION_HELP
|
||||
};
|
||||
|
||||
static struct option longopts[] =
|
||||
{
|
||||
{"help", no_argument, NULL, OPTION_HELP},
|
||||
{"version", no_argument, NULL, 'V'},
|
||||
{NULL, 0, NULL, 0}
|
||||
};
|
||||
|
||||
static void bsdar_usage(void);
|
||||
static void ranlib_usage(void);
|
||||
static void set_mode(struct bsdar *bsdar, char opt);
|
||||
static void only_mode(struct bsdar *bsdar, const char *opt,
|
||||
const char *valid_modes);
|
||||
static void bsdar_version(void);
|
||||
static void ranlib_version(void);
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
struct bsdar *bsdar, bsdar_storage;
|
||||
char *p;
|
||||
size_t len;
|
||||
int i, opt;
|
||||
|
||||
bsdar = &bsdar_storage;
|
||||
memset(bsdar, 0, sizeof(*bsdar));
|
||||
|
||||
if ((bsdar->progname = getprogname()) == NULL)
|
||||
bsdar->progname = "ar";
|
||||
|
||||
if (strcmp(bsdar->progname, "ranlib") == 0) {
|
||||
while ((opt = getopt_long(argc, argv, "tV", longopts,
|
||||
NULL)) != -1) {
|
||||
switch(opt) {
|
||||
case 't':
|
||||
/* Ignored. */
|
||||
break;
|
||||
case 'V':
|
||||
ranlib_version();
|
||||
break;
|
||||
case OPTION_HELP:
|
||||
ranlib_usage();
|
||||
default:
|
||||
ranlib_usage();
|
||||
}
|
||||
}
|
||||
argv += optind;
|
||||
argc -= optind;
|
||||
|
||||
if (*argv == NULL)
|
||||
ranlib_usage();
|
||||
|
||||
bsdar->options |= AR_S;
|
||||
for (;(bsdar->filename = *argv++) != NULL;)
|
||||
ar_mode_s(bsdar);
|
||||
|
||||
exit(EX_OK);
|
||||
} else {
|
||||
if (argc < 2)
|
||||
bsdar_usage();
|
||||
|
||||
if (*argv[1] != '-') {
|
||||
len = strlen(argv[1]) + 2;
|
||||
if ((p = malloc(len)) == NULL)
|
||||
bsdar_errc(bsdar, EX_SOFTWARE, errno,
|
||||
"malloc failed");
|
||||
*p = '-';
|
||||
(void)strlcpy(p + 1, argv[1], len - 1);
|
||||
argv[1] = p;
|
||||
}
|
||||
}
|
||||
|
||||
while ((opt = getopt_long(argc, argv, "abCcdfijlmopqrSsTtuVvxz",
|
||||
longopts, NULL)) != -1) {
|
||||
switch(opt) {
|
||||
case 'a':
|
||||
bsdar->options |= AR_A;
|
||||
break;
|
||||
case 'b':
|
||||
case 'i':
|
||||
bsdar->options |= AR_B;
|
||||
break;
|
||||
case 'C':
|
||||
bsdar->options |= AR_CC;
|
||||
break;
|
||||
case 'c':
|
||||
bsdar->options |= AR_C;
|
||||
break;
|
||||
case 'd':
|
||||
set_mode(bsdar, opt);
|
||||
break;
|
||||
case 'f':
|
||||
case 'T':
|
||||
bsdar->options |= AR_TR;
|
||||
break;
|
||||
case 'j':
|
||||
bsdar->options |= AR_J;
|
||||
break;
|
||||
case 'l':
|
||||
/* ignored, for GNU ar comptibility */
|
||||
break;
|
||||
case 'm':
|
||||
set_mode(bsdar, opt);
|
||||
break;
|
||||
case 'o':
|
||||
bsdar->options |= AR_O;
|
||||
break;
|
||||
case 'p':
|
||||
set_mode(bsdar, opt);
|
||||
break;
|
||||
case 'q':
|
||||
set_mode(bsdar, opt);
|
||||
break;
|
||||
case 'r':
|
||||
set_mode(bsdar, opt);
|
||||
break;
|
||||
case 'S':
|
||||
bsdar->options |= AR_SS;
|
||||
break;
|
||||
case 's':
|
||||
bsdar->options |= AR_S;
|
||||
break;
|
||||
case 't':
|
||||
set_mode(bsdar, opt);
|
||||
break;
|
||||
case 'u':
|
||||
bsdar->options |= AR_U;
|
||||
break;
|
||||
case 'V':
|
||||
bsdar_version();
|
||||
break;
|
||||
case 'v':
|
||||
bsdar->options |= AR_V;
|
||||
break;
|
||||
case 'x':
|
||||
set_mode(bsdar, opt);
|
||||
break;
|
||||
case 'z':
|
||||
bsdar->options |= AR_Z;
|
||||
break;
|
||||
case OPTION_HELP:
|
||||
bsdar_usage();
|
||||
default:
|
||||
bsdar_usage();
|
||||
}
|
||||
}
|
||||
|
||||
argv += optind;
|
||||
argc -= optind;
|
||||
|
||||
if (*argv == NULL)
|
||||
bsdar_usage();
|
||||
|
||||
if (bsdar->options & AR_A && bsdar->options & AR_B)
|
||||
bsdar_errc(bsdar, EX_USAGE, 0,
|
||||
"only one of -a and -[bi] options allowed");
|
||||
|
||||
if (bsdar->options & AR_J && bsdar->options & AR_Z)
|
||||
bsdar_errc(bsdar, EX_USAGE, 0,
|
||||
"only one of -j and -z options allowed");
|
||||
|
||||
if (bsdar->options & AR_S && bsdar->options & AR_SS)
|
||||
bsdar_errc(bsdar, EX_USAGE, 0,
|
||||
"only one of -s and -S options allowed");
|
||||
|
||||
if (bsdar->options & (AR_A | AR_B)) {
|
||||
if ((bsdar->posarg = *argv) == NULL)
|
||||
bsdar_errc(bsdar, EX_USAGE, 0,
|
||||
"no position operand specified");
|
||||
if ((bsdar->posarg = basename(bsdar->posarg)) == NULL)
|
||||
bsdar_errc(bsdar, EX_SOFTWARE, errno,
|
||||
"basename failed");
|
||||
argc--;
|
||||
argv++;
|
||||
}
|
||||
|
||||
if (bsdar->options & AR_A)
|
||||
only_mode(bsdar, "-a", "mqr");
|
||||
if (bsdar->options & AR_B)
|
||||
only_mode(bsdar, "-b", "mqr");
|
||||
if (bsdar->options & AR_C)
|
||||
only_mode(bsdar, "-c", "qr");
|
||||
if (bsdar->options & AR_CC)
|
||||
only_mode(bsdar, "-C", "x");
|
||||
if (bsdar->options & AR_O)
|
||||
only_mode(bsdar, "-o", "x");
|
||||
if (bsdar->options & AR_SS)
|
||||
only_mode(bsdar, "-S", "mqr");
|
||||
if (bsdar->options & AR_U)
|
||||
only_mode(bsdar, "-u", "qrx");
|
||||
|
||||
if ((bsdar->filename = *argv) == NULL)
|
||||
bsdar_usage();
|
||||
|
||||
bsdar->argc = --argc;
|
||||
bsdar->argv = ++argv;
|
||||
|
||||
if ((!bsdar->mode || strchr("ptx", bsdar->mode)) &&
|
||||
bsdar->options & AR_S) {
|
||||
ar_mode_s(bsdar);
|
||||
if (!bsdar->mode)
|
||||
exit(EX_OK);
|
||||
}
|
||||
|
||||
switch(bsdar->mode) {
|
||||
case 'd':
|
||||
ar_mode_d(bsdar);
|
||||
break;
|
||||
case 'm':
|
||||
ar_mode_m(bsdar);
|
||||
break;
|
||||
case 'p':
|
||||
ar_mode_p(bsdar);
|
||||
break;
|
||||
case 'q':
|
||||
ar_mode_r(bsdar);
|
||||
break;
|
||||
case 'r':
|
||||
ar_mode_r(bsdar);
|
||||
break;
|
||||
case 't':
|
||||
ar_mode_t(bsdar);
|
||||
break;
|
||||
case 'x':
|
||||
ar_mode_x(bsdar);
|
||||
break;
|
||||
default:
|
||||
bsdar_usage();
|
||||
/* NOTREACHED */
|
||||
}
|
||||
|
||||
for (i = 0; i < bsdar->argc; i++)
|
||||
if (bsdar->argv[i] != NULL)
|
||||
bsdar_warnc(bsdar, 0, "%s: not found in archive",
|
||||
bsdar->argv[i]);
|
||||
|
||||
exit(EX_OK);
|
||||
}
|
||||
|
||||
static void
|
||||
set_mode(struct bsdar *bsdar, char opt)
|
||||
{
|
||||
|
||||
if (bsdar->mode != '\0' && bsdar->mode != opt)
|
||||
bsdar_errc(bsdar, EX_USAGE, 0,
|
||||
"Can't specify both -%c and -%c", opt, bsdar->mode);
|
||||
bsdar->mode = opt;
|
||||
}
|
||||
|
||||
static void
|
||||
only_mode(struct bsdar *bsdar, const char *opt, const char *valid_modes)
|
||||
{
|
||||
|
||||
if (strchr(valid_modes, bsdar->mode) == NULL)
|
||||
bsdar_errc(bsdar, EX_USAGE, 0,
|
||||
"Option %s is not permitted in mode -%c", opt, bsdar->mode);
|
||||
}
|
||||
|
||||
static void
|
||||
bsdar_usage()
|
||||
{
|
||||
|
||||
(void)fprintf(stderr, "usage: ar -d [-Tjsvz] archive file ...\n");
|
||||
(void)fprintf(stderr, "\tar -m [-Tjsvz] archive file ...\n");
|
||||
(void)fprintf(stderr, "\tar -m [-Tabijsvz] position archive file ...\n");
|
||||
(void)fprintf(stderr, "\tar -p [-Tv] archive [file ...]\n");
|
||||
(void)fprintf(stderr, "\tar -r [-Tcjsuvz] archive file ...\n");
|
||||
(void)fprintf(stderr, "\tar -r [-Tabcijsuvz] position archive file ...\n");
|
||||
(void)fprintf(stderr, "\tar -s [-jz] archive\n");
|
||||
(void)fprintf(stderr, "\tar -t [-Tv] archive [file ...]\n");
|
||||
(void)fprintf(stderr, "\tar -x [-CTouv] archive [file ...]\n");
|
||||
(void)fprintf(stderr, "\tar -V\n");
|
||||
exit(EX_USAGE);
|
||||
}
|
||||
|
||||
static void
|
||||
ranlib_usage()
|
||||
{
|
||||
|
||||
(void)fprintf(stderr, "usage: ranlib [-t] archive ...\n");
|
||||
(void)fprintf(stderr, "\tranlib -V\n");
|
||||
exit(EX_USAGE);
|
||||
}
|
||||
|
||||
static void
|
||||
bsdar_version()
|
||||
{
|
||||
(void)printf("BSD ar %s - %s\n", BSDAR_VERSION, archive_version());
|
||||
exit(EX_OK);
|
||||
}
|
||||
|
||||
static void
|
||||
ranlib_version()
|
||||
{
|
||||
(void)printf("ranlib %s - %s\n", BSDAR_VERSION, archive_version());
|
||||
exit(EX_OK);
|
||||
}
|
119
usr.bin/ar/ar.h
Normal file
119
usr.bin/ar/ar.h
Normal file
@ -0,0 +1,119 @@
|
||||
/*-
|
||||
* Copyright (c) 2007 Kai Wang
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer
|
||||
* in this position and unchanged.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
/*
|
||||
* ar(1) options.
|
||||
*/
|
||||
#define AR_A 0x0001 /* position-after */
|
||||
#define AR_B 0x0002 /* position-before */
|
||||
#define AR_C 0x0004 /* creating new archive */
|
||||
#define AR_CC 0x0008 /* do not overwrite when extracting */
|
||||
#define AR_J 0x0010 /* bzip2 compression */
|
||||
#define AR_O 0x0020 /* preserve original mtime when extracting */
|
||||
#define AR_S 0x0040 /* write archive symbol table */
|
||||
#define AR_SS 0x0080 /* do not write archive symbol table */
|
||||
#define AR_TR 0x0100 /* only keep first 15 chars for member name */
|
||||
#define AR_U 0x0200 /* only extract or update newer members.*/
|
||||
#define AR_V 0x0400 /* verbose mode */
|
||||
#define AR_Z 0x0800 /* gzip compression */
|
||||
|
||||
#define DEF_BLKSZ 10240 /* default block size */
|
||||
|
||||
/*
|
||||
* Convenient wrapper for general libarchive error handling.
|
||||
*/
|
||||
#define AC(CALL) do { \
|
||||
if ((CALL)) \
|
||||
bsdar_errc(bsdar, EX_SOFTWARE, 0, "%s", \
|
||||
archive_error_string(a)); \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* In-memory representation of archive member(object).
|
||||
*/
|
||||
struct ar_obj {
|
||||
char *name; /* member name */
|
||||
void *maddr; /* mmap start address */
|
||||
uid_t uid; /* user id */
|
||||
gid_t gid; /* group id */
|
||||
mode_t md; /* octal file permissions */
|
||||
size_t size; /* member size */
|
||||
time_t mtime; /* modification time */
|
||||
int fd; /* file descriptor */
|
||||
dev_t dev; /* inode's device */
|
||||
ino_t ino; /* inode's number */
|
||||
|
||||
TAILQ_ENTRY(ar_obj) objs;
|
||||
};
|
||||
|
||||
/*
|
||||
* Structure encapsulates the "global" data for "ar" program.
|
||||
*/
|
||||
struct bsdar {
|
||||
const char *filename; /* archive name. */
|
||||
const char *posarg; /* position arg for modifiers -a, -b. */
|
||||
char mode; /* program mode */
|
||||
char compression; /* compression mode */
|
||||
int options; /* command line options */
|
||||
|
||||
const char *progname; /* program name */
|
||||
int argc;
|
||||
char **argv;
|
||||
|
||||
/*
|
||||
* Fields for the archive string table.
|
||||
*/
|
||||
char *as; /* buffer for archive string table. */
|
||||
size_t as_sz; /* current size of as table. */
|
||||
size_t as_cap; /* capacity of as table buffer. */
|
||||
|
||||
/*
|
||||
* Fields for the archive symbol table.
|
||||
*/
|
||||
uint32_t s_cnt; /* current number of symbols. */
|
||||
uint32_t *s_so; /* symbol offset table. */
|
||||
size_t s_so_cap; /* capacity of so table buffer. */
|
||||
char *s_sn; /* symbol name table */
|
||||
size_t s_sn_cap; /* capacity of sn table buffer. */
|
||||
size_t s_sn_sz; /* current size of sn table. */
|
||||
/* Current member's offset (relative to the end of pseudo members.) */
|
||||
off_t rela_off;
|
||||
|
||||
TAILQ_HEAD(, ar_obj) v_obj; /* object(member) list */
|
||||
};
|
||||
|
||||
void bsdar_errc(struct bsdar *, int _eval, int _code,
|
||||
const char *fmt, ...);
|
||||
void bsdar_warnc(struct bsdar *, int _code, const char *fmt, ...);
|
||||
void ar_mode_d(struct bsdar *bsdar);
|
||||
void ar_mode_m(struct bsdar *bsdar);
|
||||
void ar_mode_p(struct bsdar *bsdar);
|
||||
void ar_mode_r(struct bsdar *bsdar);
|
||||
void ar_mode_s(struct bsdar *bsdar);
|
||||
void ar_mode_t(struct bsdar *bsdar);
|
||||
void ar_mode_x(struct bsdar *bsdar);
|
204
usr.bin/ar/read.c
Normal file
204
usr.bin/ar/read.c
Normal file
@ -0,0 +1,204 @@
|
||||
/*-
|
||||
* Copyright (c) 2007 Kai Wang
|
||||
* Copyright (c) 2007 Tim Kientzle
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer
|
||||
* in this position and unchanged.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/queue.h>
|
||||
#include <sys/stat.h>
|
||||
#include <archive.h>
|
||||
#include <archive_entry.h>
|
||||
#include <errno.h>
|
||||
#include <libgen.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sysexits.h>
|
||||
|
||||
#include "ar.h"
|
||||
|
||||
static void read_archive(struct bsdar *bsdar, char mode);
|
||||
|
||||
void
|
||||
ar_mode_p(struct bsdar *bsdar)
|
||||
{
|
||||
|
||||
read_archive(bsdar, 'p');
|
||||
}
|
||||
|
||||
void
|
||||
ar_mode_t(struct bsdar *bsdar)
|
||||
{
|
||||
|
||||
read_archive(bsdar, 't');
|
||||
}
|
||||
|
||||
void
|
||||
ar_mode_x(struct bsdar *bsdar)
|
||||
{
|
||||
|
||||
read_archive(bsdar, 'x');
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle read modes: 'x', 't' and 'p'.
|
||||
*/
|
||||
static void
|
||||
read_archive(struct bsdar *bsdar, char mode)
|
||||
{
|
||||
struct archive *a;
|
||||
struct archive_entry *entry;
|
||||
struct stat sb;
|
||||
struct tm *tp;
|
||||
const char *bname;
|
||||
const char *name;
|
||||
mode_t md;
|
||||
size_t size;
|
||||
time_t mtime;
|
||||
uid_t uid;
|
||||
gid_t gid;
|
||||
char **av;
|
||||
char buf[25];
|
||||
char find;
|
||||
int flags, r, i;
|
||||
|
||||
if ((a = archive_read_new()) == NULL)
|
||||
bsdar_errc(bsdar, EX_SOFTWARE, 0, "archive_read_new failed");
|
||||
archive_read_support_compression_all(a);
|
||||
archive_read_support_format_all(a);
|
||||
AC(archive_read_open_file(a, bsdar->filename, DEF_BLKSZ));
|
||||
|
||||
for (;;) {
|
||||
r = archive_read_next_header(a, &entry);
|
||||
if (r == ARCHIVE_WARN || r == ARCHIVE_RETRY ||
|
||||
r == ARCHIVE_FATAL)
|
||||
bsdar_warnc(bsdar, 0, "%s", archive_error_string(a));
|
||||
if (r == ARCHIVE_EOF || r == ARCHIVE_FATAL)
|
||||
break;
|
||||
if (r == ARCHIVE_RETRY) {
|
||||
bsdar_warnc(bsdar, 0, "Retrying...");
|
||||
continue;
|
||||
}
|
||||
|
||||
name = archive_entry_pathname(entry);
|
||||
|
||||
/* Skip pseudo members. */
|
||||
if (strcmp(name, "/") == 0 || strcmp(name, "//") == 0)
|
||||
continue;
|
||||
|
||||
if (bsdar->argc > 0) {
|
||||
find = 0;
|
||||
for(i = 0; i < bsdar->argc; i++) {
|
||||
av = &bsdar->argv[i];
|
||||
if (*av == NULL)
|
||||
continue;
|
||||
if ((bname = basename(*av)) == NULL)
|
||||
bsdar_errc(bsdar, EX_SOFTWARE, errno,
|
||||
"basename failed");
|
||||
if (strcmp(bname, name) != 0)
|
||||
continue;
|
||||
|
||||
*av = NULL;
|
||||
find = 1;
|
||||
break;
|
||||
}
|
||||
if (!find)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (mode == 't') {
|
||||
if (bsdar->options & AR_V) {
|
||||
md = archive_entry_mode(entry);
|
||||
uid = archive_entry_uid(entry);
|
||||
gid = archive_entry_gid(entry);
|
||||
size = archive_entry_size(entry);
|
||||
mtime = archive_entry_mtime(entry);
|
||||
(void)strmode(md, buf);
|
||||
(void)fprintf(stdout, "%s %6d/%-6d %8ju ",
|
||||
buf + 1, uid, gid, (uintmax_t)size);
|
||||
tp = localtime(&mtime);
|
||||
(void)strftime(buf, sizeof(buf),
|
||||
"%b %e %H:%M %Y", tp);
|
||||
(void)fprintf(stdout, "%s %s", buf, name);
|
||||
} else
|
||||
(void)fprintf(stdout, "%s", name);
|
||||
r = archive_read_data_skip(a);
|
||||
if (r == ARCHIVE_WARN || r == ARCHIVE_RETRY ||
|
||||
r == ARCHIVE_FATAL) {
|
||||
(void)fprintf(stdout, "\n");
|
||||
bsdar_warnc(bsdar, 0, "%s",
|
||||
archive_error_string(a));
|
||||
}
|
||||
|
||||
if (r == ARCHIVE_FATAL)
|
||||
break;
|
||||
|
||||
(void)fprintf(stdout, "\n");
|
||||
} else {
|
||||
/* mode == 'x' || mode = 'p' */
|
||||
if (mode == 'p') {
|
||||
if (bsdar->options & AR_V) {
|
||||
(void)fprintf(stdout, "\n<%s>\n\n",
|
||||
name);
|
||||
fflush(stdout);
|
||||
}
|
||||
r = archive_read_data_into_fd(a, 1);
|
||||
} else {
|
||||
/* mode == 'x' */
|
||||
if (stat(name, &sb) != 0) {
|
||||
if (errno != ENOENT) {
|
||||
bsdar_warnc(bsdar, 0,
|
||||
"stat %s failed",
|
||||
bsdar->filename);
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
/* stat success, file exist */
|
||||
if (bsdar->options & AR_CC)
|
||||
continue;
|
||||
if (bsdar->options & AR_U &&
|
||||
archive_entry_mtime(entry) <=
|
||||
sb.st_mtime)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (bsdar->options & AR_V)
|
||||
(void)fprintf(stdout, "x - %s\n", name);
|
||||
flags = 0;
|
||||
if (bsdar->options & AR_O)
|
||||
flags |= ARCHIVE_EXTRACT_TIME;
|
||||
|
||||
r = archive_read_extract(a, entry, flags);
|
||||
}
|
||||
|
||||
if (r)
|
||||
bsdar_warnc(bsdar, 0, "%s",
|
||||
archive_error_string(a));
|
||||
}
|
||||
}
|
||||
AC(archive_read_close(a));
|
||||
AC(archive_read_finish(a));
|
||||
}
|
86
usr.bin/ar/util.c
Normal file
86
usr.bin/ar/util.c
Normal file
@ -0,0 +1,86 @@
|
||||
/*-
|
||||
* Copyright (c) 2003-2007 Tim Kientzle
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer
|
||||
* in this position and unchanged.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/queue.h>
|
||||
#include <sys/types.h>
|
||||
#include <errno.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "ar.h"
|
||||
|
||||
static void bsdar_vwarnc(struct bsdar *, int code,
|
||||
const char *fmt, va_list ap);
|
||||
static void bsdar_verrc(struct bsdar *bsdar, int code,
|
||||
const char *fmt, va_list ap);
|
||||
|
||||
static void
|
||||
bsdar_vwarnc(struct bsdar *bsdar, int code, const char *fmt, va_list ap)
|
||||
{
|
||||
|
||||
fprintf(stderr, "%s: warning: ", bsdar->progname);
|
||||
vfprintf(stderr, fmt, ap);
|
||||
if (code != 0)
|
||||
fprintf(stderr, ": %s", strerror(code));
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
|
||||
void
|
||||
bsdar_warnc(struct bsdar *bsdar, int code, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
bsdar_vwarnc(bsdar, code, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
static void
|
||||
bsdar_verrc(struct bsdar *bsdar, int code, const char *fmt, va_list ap)
|
||||
{
|
||||
|
||||
fprintf(stderr, "%s: fatal: ", bsdar->progname);
|
||||
vfprintf(stderr, fmt, ap);
|
||||
if (code != 0)
|
||||
fprintf(stderr, ": %s", strerror(code));
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
|
||||
void
|
||||
bsdar_errc(struct bsdar *bsdar, int eval, int code, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
bsdar_verrc(bsdar, code, fmt, ap);
|
||||
va_end(ap);
|
||||
exit(eval);
|
||||
}
|
784
usr.bin/ar/write.c
Normal file
784
usr.bin/ar/write.c
Normal file
@ -0,0 +1,784 @@
|
||||
/*-
|
||||
* Copyright (c) 2007 Kai Wang
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer
|
||||
* in this position and unchanged.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/endian.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/queue.h>
|
||||
#include <sys/stat.h>
|
||||
#include <archive.h>
|
||||
#include <archive_entry.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <gelf.h>
|
||||
#include <libgen.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sysexits.h>
|
||||
|
||||
#include "ar.h"
|
||||
|
||||
#define _ARMAG_LEN 8 /* length of ar magic string */
|
||||
#define _ARHDR_LEN 60 /* length of ar header */
|
||||
#define _INIT_AS_CAP 128 /* initial archive string table size */
|
||||
#define _INIT_SYMOFF_CAP (256*(sizeof(uint32_t))) /* initial so table size */
|
||||
#define _INIT_SYMNAME_CAP 1024 /* initial sn table size */
|
||||
#define _MAXNAMELEN_SVR4 15 /* max member name length in svr4 variant */
|
||||
#define _TRUNCATE_LEN 15 /* number of bytes to keep for member name */
|
||||
|
||||
static void add_to_ar_str_table(struct bsdar *bsdar, const char *name);
|
||||
static void add_to_ar_sym_table(struct bsdar *bsdar, const char *name);
|
||||
static struct ar_obj *create_obj_from_file(struct bsdar *bsdar,
|
||||
const char *name, time_t mtime);
|
||||
static void create_symtab_entry(struct bsdar *bsdar, void *maddr,
|
||||
size_t size);
|
||||
static void insert_obj(struct bsdar *bsdar, struct ar_obj *obj,
|
||||
struct ar_obj *pos);
|
||||
static void write_archive(struct bsdar *bsdar, char mode);
|
||||
static void write_cleanup(struct bsdar *bsdar);
|
||||
static void write_data(struct bsdar *bsdar, struct archive *a,
|
||||
const void *buf, size_t s);
|
||||
static void write_objs(struct bsdar *bsdar);
|
||||
|
||||
void
|
||||
ar_mode_d(struct bsdar *bsdar)
|
||||
{
|
||||
|
||||
write_archive(bsdar, 'd');
|
||||
}
|
||||
|
||||
void
|
||||
ar_mode_m(struct bsdar *bsdar)
|
||||
{
|
||||
|
||||
write_archive(bsdar, 'm');
|
||||
}
|
||||
|
||||
void
|
||||
ar_mode_r(struct bsdar *bsdar)
|
||||
{
|
||||
|
||||
write_archive(bsdar, 'r');
|
||||
}
|
||||
|
||||
void
|
||||
ar_mode_s(struct bsdar *bsdar)
|
||||
{
|
||||
|
||||
write_archive(bsdar, 's');
|
||||
}
|
||||
|
||||
/*
|
||||
* Create object from file, return created obj upon success, or NULL
|
||||
* when an error occurs or the member is not newer than existing
|
||||
* one while -u is specifed.
|
||||
*/
|
||||
static struct ar_obj *
|
||||
create_obj_from_file(struct bsdar *bsdar, const char *name, time_t mtime)
|
||||
{
|
||||
struct ar_obj *obj;
|
||||
struct stat sb;
|
||||
const char *bname;
|
||||
|
||||
if (name == NULL)
|
||||
return(NULL);
|
||||
|
||||
obj = malloc(sizeof(struct ar_obj));
|
||||
if (obj == NULL)
|
||||
bsdar_errc(bsdar, EX_SOFTWARE, errno, "malloc failed");
|
||||
if ((obj->fd = open(name, O_RDONLY, 0)) < 0) {
|
||||
bsdar_warnc(bsdar, errno, "can't open file: %s", name);
|
||||
free(obj);
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
if ((bname = basename(name)) == NULL)
|
||||
bsdar_errc(bsdar, EX_SOFTWARE, errno, "basename failed");
|
||||
if (bsdar->options & AR_TR && strlen(bname) > _TRUNCATE_LEN) {
|
||||
if ((obj->name = malloc(_TRUNCATE_LEN + 1)) == NULL)
|
||||
bsdar_errc(bsdar, EX_SOFTWARE, errno, "malloc failed");
|
||||
(void)strncpy(obj->name, bname, _TRUNCATE_LEN);
|
||||
obj->name[_TRUNCATE_LEN] = '\0';
|
||||
} else
|
||||
if ((obj->name = strdup(bname)) == NULL)
|
||||
bsdar_errc(bsdar, EX_SOFTWARE, errno, "strdup failed");
|
||||
|
||||
if (fstat(obj->fd, &sb) < 0) {
|
||||
bsdar_warnc(bsdar, errno, "can't fstat file: %s", obj->name);
|
||||
goto giveup;
|
||||
}
|
||||
if (!S_ISREG(sb.st_mode)) {
|
||||
bsdar_warnc(bsdar, 0, "%s is not an ordinary file", obj->name);
|
||||
goto giveup;
|
||||
}
|
||||
|
||||
/*
|
||||
* When option '-u' is specified and member is not newer than the
|
||||
* existing one, the replace will not happen. While if mtime == 0,
|
||||
* which indicates that this is to "replace a none exist member",
|
||||
* the replace will proceed regardless of '-u'.
|
||||
*/
|
||||
if (mtime != 0 && bsdar->options & AR_U && sb.st_mtime <= mtime)
|
||||
goto giveup;
|
||||
|
||||
obj->uid = sb.st_uid;
|
||||
obj->gid = sb.st_gid;
|
||||
obj->md = sb.st_mode;
|
||||
obj->size = sb.st_size;
|
||||
obj->mtime = sb.st_mtime;
|
||||
obj->dev = sb.st_dev;
|
||||
obj->ino = sb.st_ino;
|
||||
if ((obj->maddr = mmap(NULL, obj->size, PROT_READ,
|
||||
MAP_PRIVATE, obj->fd, (off_t)0)) == MAP_FAILED) {
|
||||
bsdar_warnc(bsdar, errno, "can't mmap file: %s", obj->name);
|
||||
goto giveup;
|
||||
}
|
||||
if (close(obj->fd) < 0)
|
||||
bsdar_errc(bsdar, EX_SOFTWARE, errno, "close failed: %s",
|
||||
obj->name);
|
||||
|
||||
return(obj);
|
||||
|
||||
giveup:
|
||||
if (close(obj->fd) < 0)
|
||||
bsdar_errc(bsdar, EX_SOFTWARE, errno, "close failed: %s",
|
||||
obj->name);
|
||||
free(obj->name);
|
||||
free(obj);
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Insert obj to the tail, or before/after the pos obj.
|
||||
*/
|
||||
static void
|
||||
insert_obj(struct bsdar *bsdar, struct ar_obj *obj, struct ar_obj *pos)
|
||||
{
|
||||
if (obj == NULL)
|
||||
bsdar_errc(bsdar, EX_SOFTWARE, 0, "try to insert a null obj");
|
||||
|
||||
if (pos == NULL || obj == pos)
|
||||
/*
|
||||
* If the object to move happens to be the posistion obj,
|
||||
* or if there is not a pos obj, move it to tail.
|
||||
*/
|
||||
goto tail;
|
||||
|
||||
if (bsdar->options & AR_B) {
|
||||
TAILQ_INSERT_BEFORE(pos, obj, objs);
|
||||
return;
|
||||
}
|
||||
if (bsdar->options & AR_A) {
|
||||
TAILQ_INSERT_AFTER(&bsdar->v_obj, pos, obj, objs);
|
||||
return;
|
||||
}
|
||||
|
||||
tail:
|
||||
TAILQ_INSERT_TAIL(&bsdar->v_obj, obj, objs);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Determine the constitution of resulting archive.
|
||||
*/
|
||||
static void
|
||||
write_archive(struct bsdar *bsdar, char mode)
|
||||
{
|
||||
struct archive *a;
|
||||
struct archive_entry *entry;
|
||||
struct ar_obj *nobj, *obj, *obj_temp, *pos;
|
||||
struct stat sb;
|
||||
const char *name;
|
||||
const char *bname;
|
||||
char *buff;
|
||||
char **av;
|
||||
size_t size;
|
||||
int i, r;
|
||||
|
||||
TAILQ_INIT(&bsdar->v_obj);
|
||||
nobj = NULL;
|
||||
pos = NULL;
|
||||
memset(&sb, 0, sizeof(sb));
|
||||
|
||||
/* By default, no compression is assumed. */
|
||||
bsdar->compression = ARCHIVE_COMPRESSION_NONE;
|
||||
|
||||
/*
|
||||
* Test if the specified archive exists, to figure out
|
||||
* whether we are creating one here.
|
||||
*/
|
||||
if (stat(bsdar->filename, &sb) != 0) {
|
||||
if (errno != ENOENT) {
|
||||
bsdar_warnc(bsdar, 0, "stat %s failed",
|
||||
bsdar->filename);
|
||||
return;
|
||||
}
|
||||
|
||||
/* We do not create archive in mode 'd', 'm' and 's'. */
|
||||
if (mode != 'r') {
|
||||
bsdar_warnc(bsdar, 0, "%s: no such file",
|
||||
bsdar->filename);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Issue a warning if -c is not specified when creating. */
|
||||
if (!(bsdar->options & AR_C))
|
||||
bsdar_warnc(bsdar, 0, "creating %s", bsdar->filename);
|
||||
goto new_archive;
|
||||
}
|
||||
|
||||
/*
|
||||
* First read members from existing archive.
|
||||
*/
|
||||
if ((a = archive_read_new()) == NULL)
|
||||
bsdar_errc(bsdar, EX_SOFTWARE, 0, "archive_read_new failed");
|
||||
archive_read_support_compression_all(a);
|
||||
archive_read_support_format_ar(a);
|
||||
AC(archive_read_open_filename(a, bsdar->filename, DEF_BLKSZ));
|
||||
for (;;) {
|
||||
r = archive_read_next_header(a, &entry);
|
||||
if (r == ARCHIVE_FATAL)
|
||||
bsdar_errc(bsdar, EX_DATAERR, 0, "%s",
|
||||
archive_error_string(a));
|
||||
if (r == ARCHIVE_EOF)
|
||||
break;
|
||||
if (r == ARCHIVE_WARN || r == ARCHIVE_RETRY)
|
||||
bsdar_warnc(bsdar, 0, "%s", archive_error_string(a));
|
||||
if (r == ARCHIVE_RETRY) {
|
||||
bsdar_warnc(bsdar, 0, "Retrying...");
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Remember the compression mode of existing archive.
|
||||
* If neither -j nor -z is specified, this mode will
|
||||
* be used for resulting archive.
|
||||
*/
|
||||
bsdar->compression = archive_compression(a);
|
||||
|
||||
name = archive_entry_pathname(entry);
|
||||
|
||||
/*
|
||||
* skip pseudo members.
|
||||
*/
|
||||
if (strcmp(name, "/") == 0 || strcmp(name, "//") == 0)
|
||||
continue;
|
||||
|
||||
size = archive_entry_size(entry);
|
||||
|
||||
if ((buff = malloc(size)) == NULL)
|
||||
bsdar_errc(bsdar, EX_SOFTWARE, errno, "malloc failed");
|
||||
if (archive_read_data(a, buff, size) != (ssize_t)size) {
|
||||
bsdar_warnc(bsdar, 0, "%s", archive_error_string(a));
|
||||
free(buff);
|
||||
continue;
|
||||
}
|
||||
obj = malloc(sizeof(struct ar_obj));
|
||||
if (obj == NULL)
|
||||
bsdar_errc(bsdar, EX_SOFTWARE, errno, "malloc failed");
|
||||
obj->maddr = buff;
|
||||
if ((obj->name = strdup(name)) == NULL)
|
||||
bsdar_errc(bsdar, EX_SOFTWARE, errno, "strdup failed");
|
||||
obj->size = size;
|
||||
obj->uid = archive_entry_uid(entry);
|
||||
obj->gid = archive_entry_gid(entry);
|
||||
obj->md = archive_entry_mode(entry);
|
||||
obj->mtime = archive_entry_mtime(entry);
|
||||
obj->dev = 0;
|
||||
obj->ino = 0;
|
||||
|
||||
/*
|
||||
* Objects from archive have obj->fd set to -1,
|
||||
* for the ease of cleaning up.
|
||||
*/
|
||||
obj->fd = -1;
|
||||
TAILQ_INSERT_TAIL(&bsdar->v_obj, obj, objs);
|
||||
}
|
||||
AC(archive_read_close(a));
|
||||
AC(archive_read_finish(a));
|
||||
|
||||
/*
|
||||
* For mode 's', no member will be moved, deleted or replaced.
|
||||
*/
|
||||
if (mode == 's')
|
||||
goto write_objs;
|
||||
|
||||
/*
|
||||
* Try to find the position member specified by user.
|
||||
*/
|
||||
if (bsdar->options & AR_A || bsdar->options & AR_B) {
|
||||
TAILQ_FOREACH(obj, &bsdar->v_obj, objs) {
|
||||
if (strcmp(obj->name, bsdar->posarg) == 0) {
|
||||
pos = obj;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If can't find `pos' specified by user,
|
||||
* sliently insert objects at tail.
|
||||
*/
|
||||
if (pos == NULL)
|
||||
bsdar->options &= ~(AR_A | AR_B);
|
||||
}
|
||||
|
||||
for (i = 0; i < bsdar->argc; i++) {
|
||||
av = &bsdar->argv[i];
|
||||
|
||||
TAILQ_FOREACH_SAFE(obj, &bsdar->v_obj, objs, obj_temp) {
|
||||
if ((bname = basename(*av)) == NULL)
|
||||
bsdar_errc(bsdar, EX_SOFTWARE, errno,
|
||||
"basename failed");
|
||||
if (bsdar->options & AR_TR) {
|
||||
if (strncmp(bname, obj->name, _TRUNCATE_LEN))
|
||||
continue;
|
||||
} else
|
||||
if (strcmp(bname, obj->name) != 0)
|
||||
continue;
|
||||
|
||||
if (mode == 'r') {
|
||||
/*
|
||||
* if the new member is not qualified
|
||||
* to replace the old one, skip it.
|
||||
*/
|
||||
nobj = create_obj_from_file(bsdar, *av,
|
||||
obj->mtime);
|
||||
if (nobj == NULL)
|
||||
goto skip_obj;
|
||||
}
|
||||
|
||||
if (bsdar->options & AR_V)
|
||||
(void)fprintf(stdout, "%c - %s\n", mode,
|
||||
*av);
|
||||
|
||||
TAILQ_REMOVE(&bsdar->v_obj, obj, objs);
|
||||
if (mode == 'd' || mode == 'r') {
|
||||
free(obj->maddr);
|
||||
free(obj->name);
|
||||
free(obj);
|
||||
}
|
||||
|
||||
if (mode == 'm')
|
||||
insert_obj(bsdar, obj, pos);
|
||||
if (mode == 'r')
|
||||
insert_obj(bsdar, nobj, pos);
|
||||
|
||||
skip_obj:
|
||||
*av = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
new_archive:
|
||||
/*
|
||||
* When operating in mode 'r', directly add those user specified
|
||||
* objects which do not exist in current archive.
|
||||
*/
|
||||
for (i = 0; i < bsdar->argc; i++) {
|
||||
av = &bsdar->argv[i];
|
||||
if (*av != NULL && mode == 'r') {
|
||||
nobj = create_obj_from_file(bsdar, *av, 0);
|
||||
if (nobj != NULL)
|
||||
insert_obj(bsdar, nobj, pos);
|
||||
if (bsdar->options & AR_V && nobj != NULL)
|
||||
(void)fprintf(stdout, "a - %s\n", *av);
|
||||
*av = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
write_objs:
|
||||
write_objs(bsdar);
|
||||
write_cleanup(bsdar);
|
||||
}
|
||||
|
||||
/*
|
||||
* Memory cleaning up.
|
||||
*/
|
||||
static void
|
||||
write_cleanup(struct bsdar *bsdar)
|
||||
{
|
||||
struct ar_obj *obj, *obj_temp;
|
||||
|
||||
TAILQ_FOREACH_SAFE(obj, &bsdar->v_obj, objs, obj_temp) {
|
||||
free(obj->name);
|
||||
if (obj->fd == -1)
|
||||
free(obj->maddr);
|
||||
else
|
||||
if (munmap(obj->maddr, obj->size))
|
||||
bsdar_warnc(bsdar, errno,
|
||||
"can't munmap file: %s", obj->name);
|
||||
TAILQ_REMOVE(&bsdar->v_obj, obj, objs);
|
||||
free(obj);
|
||||
}
|
||||
|
||||
free(bsdar->as);
|
||||
free(bsdar->s_so);
|
||||
free(bsdar->s_sn);
|
||||
bsdar->as = NULL;
|
||||
bsdar->s_so = NULL;
|
||||
bsdar->s_sn = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Wrapper for archive_write_data().
|
||||
*/
|
||||
static void
|
||||
write_data(struct bsdar *bsdar, struct archive *a, const void *buf, size_t s)
|
||||
{
|
||||
if (archive_write_data(a, buf, s) != (ssize_t)s)
|
||||
bsdar_errc(bsdar, EX_SOFTWARE, 0, "%s",
|
||||
archive_error_string(a));
|
||||
}
|
||||
|
||||
/*
|
||||
* Write the resulting archive members.
|
||||
*/
|
||||
static void
|
||||
write_objs(struct bsdar *bsdar)
|
||||
{
|
||||
struct ar_obj *obj;
|
||||
struct archive *a;
|
||||
struct archive_entry *entry;
|
||||
size_t s_sz; /* size of archive symbol table. */
|
||||
size_t pm_sz; /* size of pseudo members */
|
||||
int i, nr;
|
||||
|
||||
if (elf_version(EV_CURRENT) == EV_NONE)
|
||||
bsdar_errc(bsdar, EX_SOFTWARE, 0,
|
||||
"ELF library initialization failed: %s", elf_errmsg(-1));
|
||||
|
||||
bsdar->rela_off = 0;
|
||||
|
||||
/* Create archive symbol table and archive string table, if need. */
|
||||
TAILQ_FOREACH(obj, &bsdar->v_obj, objs) {
|
||||
if (!(bsdar->options & AR_SS))
|
||||
create_symtab_entry(bsdar, obj->maddr, obj->size);
|
||||
if (strlen(obj->name) > _MAXNAMELEN_SVR4)
|
||||
add_to_ar_str_table(bsdar, obj->name);
|
||||
bsdar->rela_off += _ARHDR_LEN + obj->size + obj->size % 2;
|
||||
}
|
||||
|
||||
/*
|
||||
* Pad the symbol name string table. It is treated specially because
|
||||
* symbol name table should be padded by a '\0', not the common '\n'
|
||||
* for other members. The size of sn table includes the pad bit.
|
||||
*/
|
||||
if (bsdar->s_cnt != 0 && bsdar->s_sn_sz % 2 != 0)
|
||||
bsdar->s_sn[bsdar->s_sn_sz++] = '\0';
|
||||
|
||||
/*
|
||||
* Archive string table is padded by a "\n" as the normal members.
|
||||
* The differece is that the size of archive string table counts
|
||||
* in the pad bit, while normal members' size fileds do not.
|
||||
*/
|
||||
if (bsdar->as != NULL && bsdar->as_sz % 2 != 0)
|
||||
bsdar->as[bsdar->as_sz++] = '\n';
|
||||
|
||||
/*
|
||||
* If there is a symbol table, calculate the size of pseudo members,
|
||||
* covert previously stored relative offsets to absolute ones, and
|
||||
* then make them Big Endian.
|
||||
*
|
||||
* absolute_offset = htobe32(relative_offset + size_of_pseudo_members)
|
||||
*/
|
||||
|
||||
if (bsdar->s_cnt != 0) {
|
||||
s_sz = (bsdar->s_cnt + 1) * sizeof(uint32_t) + bsdar->s_sn_sz;
|
||||
pm_sz = _ARMAG_LEN + (_ARHDR_LEN + s_sz);
|
||||
if (bsdar->as != NULL)
|
||||
pm_sz += _ARHDR_LEN + bsdar->as_sz;
|
||||
for (i = 0; (size_t)i < bsdar->s_cnt; i++)
|
||||
*(bsdar->s_so + i) = htobe32(*(bsdar->s_so + i) +
|
||||
pm_sz);
|
||||
}
|
||||
|
||||
if ((a = archive_write_new()) == NULL)
|
||||
bsdar_errc(bsdar, EX_SOFTWARE, 0, "archive_write_new failed");
|
||||
|
||||
archive_write_set_format_ar_svr4(a);
|
||||
|
||||
/* The compression mode of the existing archive is used
|
||||
* for the result archive or if creating a new archive, we
|
||||
* do not compress archive by default. This default behavior can
|
||||
* be overrided by compression mode specified explicitly
|
||||
* through command line option `-j' or `-z'.
|
||||
*/
|
||||
if (bsdar->options & AR_J)
|
||||
bsdar->compression = ARCHIVE_COMPRESSION_BZIP2;
|
||||
if (bsdar->options & AR_Z)
|
||||
bsdar->compression = ARCHIVE_COMPRESSION_GZIP;
|
||||
if (bsdar->compression == ARCHIVE_COMPRESSION_BZIP2)
|
||||
archive_write_set_compression_bzip2(a);
|
||||
else if (bsdar->compression == ARCHIVE_COMPRESSION_GZIP)
|
||||
archive_write_set_compression_gzip(a);
|
||||
else
|
||||
archive_write_set_compression_none(a);
|
||||
|
||||
AC(archive_write_open_filename(a, bsdar->filename));
|
||||
|
||||
/*
|
||||
* write the archive symbol table, if there is one.
|
||||
* If options -s is explicitly specified or we are invoked
|
||||
* as ranlib, write the symbol table even if it is empty.
|
||||
*/
|
||||
if ((bsdar->s_cnt != 0 && !(bsdar->options & AR_SS)) ||
|
||||
bsdar->options & AR_S) {
|
||||
entry = archive_entry_new();
|
||||
archive_entry_copy_pathname(entry, "/");
|
||||
archive_entry_set_mtime(entry, time(NULL), 0);
|
||||
archive_entry_set_size(entry, (bsdar->s_cnt + 1) *
|
||||
sizeof(uint32_t) + bsdar->s_sn_sz);
|
||||
AC(archive_write_header(a, entry));
|
||||
nr = htobe32(bsdar->s_cnt);
|
||||
write_data(bsdar, a, &nr, sizeof(uint32_t));
|
||||
write_data(bsdar, a, bsdar->s_so, sizeof(uint32_t) *
|
||||
bsdar->s_cnt);
|
||||
write_data(bsdar, a, bsdar->s_sn, bsdar->s_sn_sz);
|
||||
archive_entry_free(entry);
|
||||
}
|
||||
|
||||
/* write the archive string table, if any. */
|
||||
if (bsdar->as != NULL) {
|
||||
entry = archive_entry_new();
|
||||
archive_entry_copy_pathname(entry, "//");
|
||||
archive_entry_set_size(entry, bsdar->as_sz);
|
||||
AC(archive_write_header(a, entry));
|
||||
write_data(bsdar, a, bsdar->as, bsdar->as_sz);
|
||||
archive_entry_free(entry);
|
||||
}
|
||||
|
||||
/* write normal members. */
|
||||
TAILQ_FOREACH(obj, &bsdar->v_obj, objs) {
|
||||
entry = archive_entry_new();
|
||||
archive_entry_copy_pathname(entry, obj->name);
|
||||
archive_entry_set_uid(entry, obj->uid);
|
||||
archive_entry_set_gid(entry, obj->gid);
|
||||
archive_entry_set_mode(entry, obj->md);
|
||||
archive_entry_set_size(entry, obj->size);
|
||||
archive_entry_set_mtime(entry, obj->mtime, 0);
|
||||
archive_entry_set_dev(entry, obj->dev);
|
||||
archive_entry_set_ino(entry, obj->ino);
|
||||
archive_entry_set_filetype(entry, AE_IFREG);
|
||||
AC(archive_write_header(a, entry));
|
||||
write_data(bsdar, a, obj->maddr, obj->size);
|
||||
archive_entry_free(entry);
|
||||
}
|
||||
|
||||
AC(archive_write_close(a));
|
||||
AC(archive_write_finish(a));
|
||||
}
|
||||
|
||||
/*
|
||||
* Extract global symbols from ELF binary members.
|
||||
*/
|
||||
static void
|
||||
create_symtab_entry(struct bsdar *bsdar, void *maddr, size_t size)
|
||||
{
|
||||
Elf *e;
|
||||
Elf_Scn *scn;
|
||||
GElf_Shdr shdr;
|
||||
GElf_Sym sym;
|
||||
Elf_Data *data;
|
||||
char *name;
|
||||
size_t n, shstrndx;
|
||||
int elferr, tabndx, len, i;
|
||||
|
||||
if ((e = elf_memory(maddr, size)) == NULL) {
|
||||
bsdar_warnc(bsdar, 0, "elf_memory() failed: %s",
|
||||
elf_errmsg(-1));
|
||||
return;
|
||||
}
|
||||
if (elf_kind(e) != ELF_K_ELF) {
|
||||
/* Sliently ignore non-elf member. */
|
||||
elf_end(e);
|
||||
return;
|
||||
}
|
||||
if (elf_getshstrndx(e, &shstrndx) == 0) {
|
||||
bsdar_warnc(bsdar, EX_SOFTWARE, 0, "elf_getshstrndx failed: %s",
|
||||
elf_errmsg(-1));
|
||||
elf_end(e);
|
||||
return;
|
||||
}
|
||||
|
||||
tabndx = -1;
|
||||
scn = NULL;
|
||||
while ((scn = elf_nextscn(e, scn)) != NULL) {
|
||||
if (gelf_getshdr(scn, &shdr) != &shdr) {
|
||||
bsdar_warnc(bsdar, 0,
|
||||
"elf_getshdr failed: %s", elf_errmsg(-1));
|
||||
continue;
|
||||
}
|
||||
if ((name = elf_strptr(e, shstrndx, shdr.sh_name)) == NULL) {
|
||||
bsdar_warnc(bsdar, 0,
|
||||
"elf_strptr failed: %s", elf_errmsg(-1));
|
||||
continue;
|
||||
}
|
||||
if (strcmp(name, ".strtab") == 0) {
|
||||
tabndx = elf_ndxscn(scn);
|
||||
break;
|
||||
}
|
||||
}
|
||||
elferr = elf_errno();
|
||||
if (elferr != 0)
|
||||
bsdar_warnc(bsdar, 0, "elf_nextscn failed: %s",
|
||||
elf_errmsg(elferr));
|
||||
if (tabndx == -1) {
|
||||
bsdar_warnc(bsdar, 0, "can't find .strtab section");
|
||||
elf_end(e);
|
||||
return;
|
||||
}
|
||||
|
||||
scn = NULL;
|
||||
while ((scn = elf_nextscn(e, scn)) != NULL) {
|
||||
if (gelf_getshdr(scn, &shdr) != &shdr) {
|
||||
bsdar_warnc(bsdar, EX_SOFTWARE, 0,
|
||||
"elf_getshdr failed: %s", elf_errmsg(-1));
|
||||
continue;
|
||||
}
|
||||
if (shdr.sh_type != SHT_SYMTAB)
|
||||
continue;
|
||||
|
||||
data = NULL;
|
||||
n = 0;
|
||||
while (n < shdr.sh_size &&
|
||||
(data = elf_getdata(scn, data)) != NULL) {
|
||||
len = data->d_size / shdr.sh_entsize;
|
||||
for (i = 0; i < len; i++) {
|
||||
if (gelf_getsym(data, i, &sym) != &sym) {
|
||||
bsdar_warnc(bsdar, EX_SOFTWARE, 0,
|
||||
"gelf_getsym failed: %s",
|
||||
elf_errmsg(-1));
|
||||
continue;
|
||||
}
|
||||
|
||||
/* keep only global or weak symbols */
|
||||
if (GELF_ST_BIND(sym.st_info) != STB_GLOBAL &&
|
||||
GELF_ST_BIND(sym.st_info) != STB_WEAK)
|
||||
continue;
|
||||
|
||||
/* keep only defined symbols */
|
||||
if (sym.st_shndx == SHN_UNDEF)
|
||||
continue;
|
||||
|
||||
if ((name = elf_strptr(e, tabndx,
|
||||
sym.st_name)) == NULL) {
|
||||
bsdar_warnc(bsdar, EX_SOFTWARE, 0,
|
||||
"elf_strptr failed: %s",
|
||||
elf_errmsg(-1));
|
||||
continue;
|
||||
}
|
||||
|
||||
add_to_ar_sym_table(bsdar, name);
|
||||
}
|
||||
}
|
||||
}
|
||||
elferr = elf_errno();
|
||||
if (elferr != 0)
|
||||
bsdar_warnc(bsdar, EX_SOFTWARE, 0, "elf_nextscn failed: %s",
|
||||
elf_errmsg(elferr));
|
||||
|
||||
elf_end(e);
|
||||
}
|
||||
|
||||
/*
|
||||
* Append to the archive string table buffer.
|
||||
*/
|
||||
static void
|
||||
add_to_ar_str_table(struct bsdar *bsdar, const char *name)
|
||||
{
|
||||
|
||||
if (bsdar->as == NULL) {
|
||||
bsdar->as_cap = _INIT_AS_CAP;
|
||||
bsdar->as_sz = 0;
|
||||
if ((bsdar->as = malloc(bsdar->as_cap)) == NULL)
|
||||
bsdar_errc(bsdar, EX_SOFTWARE, errno, "malloc failed");
|
||||
}
|
||||
|
||||
/*
|
||||
* The space required for holding one member name in as table includes:
|
||||
* strlen(name) + (1 for '/') + (1 for '\n') + (possibly 1 for padding).
|
||||
*/
|
||||
if (bsdar->as_sz + strlen(name) + 3 > bsdar->as_cap) {
|
||||
bsdar->as_cap *= 2;
|
||||
bsdar->as = realloc(bsdar->as, bsdar->as_cap);
|
||||
if (bsdar->as == NULL)
|
||||
bsdar_errc(bsdar, EX_SOFTWARE, errno, "realloc failed");
|
||||
}
|
||||
strncpy(&bsdar->as[bsdar->as_sz], name, strlen(name));
|
||||
bsdar->as_sz += strlen(name);
|
||||
bsdar->as[bsdar->as_sz++] = '/';
|
||||
bsdar->as[bsdar->as_sz++] = '\n';
|
||||
}
|
||||
|
||||
/*
|
||||
* Append to the archive symbol table buffer.
|
||||
*/
|
||||
static void
|
||||
add_to_ar_sym_table(struct bsdar *bsdar, const char *name)
|
||||
{
|
||||
|
||||
if (bsdar->s_so == NULL) {
|
||||
if ((bsdar->s_so = malloc(_INIT_SYMOFF_CAP)) ==
|
||||
NULL)
|
||||
bsdar_errc(bsdar, EX_SOFTWARE, errno, "malloc failed");
|
||||
bsdar->s_so_cap = _INIT_SYMOFF_CAP;
|
||||
bsdar->s_cnt = 0;
|
||||
}
|
||||
|
||||
if (bsdar->s_sn == NULL) {
|
||||
if ((bsdar->s_sn = malloc(_INIT_SYMNAME_CAP)) == NULL)
|
||||
bsdar_errc(bsdar, EX_SOFTWARE, errno, "malloc failed");
|
||||
bsdar->s_sn_cap = _INIT_SYMNAME_CAP;
|
||||
bsdar->s_sn_sz = 0;
|
||||
}
|
||||
|
||||
if (bsdar->s_cnt * sizeof(uint32_t) >= bsdar->s_so_cap) {
|
||||
bsdar->s_so_cap *= 2;
|
||||
bsdar->s_so = realloc(bsdar->s_so, bsdar->s_so_cap);
|
||||
if (bsdar->s_so == NULL)
|
||||
bsdar_errc(bsdar, EX_SOFTWARE, errno, "realloc failed");
|
||||
}
|
||||
bsdar->s_so[bsdar->s_cnt] = bsdar->rela_off;
|
||||
bsdar->s_cnt++;
|
||||
|
||||
/*
|
||||
* The space required for holding one symbol name in sn table includes:
|
||||
* strlen(name) + (1 for '\n') + (possibly 1 for padding).
|
||||
*/
|
||||
if (bsdar->s_sn_sz + strlen(name) + 2 > bsdar->s_sn_cap) {
|
||||
bsdar->s_sn_cap *= 2;
|
||||
bsdar->s_sn = realloc(bsdar->s_sn, bsdar->s_sn_cap);
|
||||
if (bsdar->s_sn == NULL)
|
||||
bsdar_errc(bsdar, EX_SOFTWARE, errno, "realloc failed");
|
||||
}
|
||||
strncpy(&bsdar->s_sn[bsdar->s_sn_sz], name, strlen(name));
|
||||
bsdar->s_sn_sz += strlen(name);
|
||||
bsdar->s_sn[bsdar->s_sn_sz++] = '\0';
|
||||
}
|
Loading…
Reference in New Issue
Block a user