ack/util/arch/archiver.c
2019-03-24 23:36:52 +08:00

924 lines
17 KiB
C

/*
* (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands.
* See the copyright notice in the ACK home directory, in the file "Copyright".
*/
/* ar - archiver Author: Michiel Huisjes */
/* Made into arch/aal by Ceriel Jacobs
*/
/*
* Usage: [arch|aal] [qdprtx][vlcu] archive [file] ...
* possible key
* d: delete
* p: print named files
* q: append
* r: replace (append when not in archive)
* t: print contents of archive
* x: extract
* possible args
* c: don't give "create" message
* u: replace only if dated later than member in archive
* v: verbose
#ifdef DISTRIBUTION
* D: make distribution: use distr_time, uid=2, gid=2, mode=0644
#endif
*/
#include <fcntl.h>
#include <stdlib.h>
#include <time.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <signal.h>
#include "print.h"
#include "system.h"
#include "object.h"
#include "arch.h"
#include "ranlib.h"
/* UNIX specific */
#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__)))
#ifndef unix
#define unix
#endif
#endif
#ifdef unix
#include <unistd.h>
#else
#define getuid() 0
#define getgid() 0
#endif
#ifdef AAL
#include "out.h"
#define MAGIC_NUMBER AALMAG
long offset;
struct ranlib *tab;
unsigned int tnum = 0;
char *tstrtab;
unsigned int tssiz = 0;
unsigned int tabsz, strtabsz;
#else
#define MAGIC_NUMBER ARMAG
#endif
#define odd(nr) (nr & 01)
#define even(nr) (odd(nr) ? nr + 1 : nr)
typedef char BOOL;
#define FALSE 0
#define TRUE 1
#define READ 0
#define APPEND 2
#define CREATE 1
#define MEMBER struct ar_hdr
#define NIL_PTR ((char *) 0)
#define NIL_MEM ((MEMBER *) 0)
#define NIL_LONG ((long *) 0)
#define IO_SIZE (10 * 1024)
#define equal(str1, str2) (!strncmp((str1), (str2), AR_NAME_MAX))
BOOL verbose;
BOOL app_fl;
BOOL ex_fl;
BOOL show_fl;
/* print files found in archive. */
BOOL pr_fl;
BOOL u_fl;
BOOL rep_fl;
BOOL del_fl;
BOOL nocr_fl;
BOOL local_fl;
#ifdef DISTRIBUTION
BOOL distr_fl;
time_t distr_time;
#endif
#ifndef S_ISUID
#define S_ISUID 0
#endif
#ifndef S_ISGID
#define S_ISGID 0
#endif
#define MODE_COUNT 11
char io_buffer[IO_SIZE];
char *progname;
char temp_buf[L_tmpnam];
char *temp_arch = &temp_buf[0];
void do_object(FILE* f, long size);
void do_names(struct outhead *headp);
void enter_name(struct outname *namep);
void write_symdef(FILE *ar);
void error(BOOL quit, char *str1, char *str2);
FILE* open_archive(char *name, int mode);
void
catch(int param);
MEMBER *get_member(FILE*);
void get(int argc, char *argv[]);
void add(char *name, FILE* ar, FILE* dst, char *mess);
void extract(FILE* ar, MEMBER *member);
void copy_member(MEMBER *member, FILE* from, FILE* to, BOOL extracting);
char *get_mode(int mode);
void wr_fatal(void);
void rd_fatal(void);
void mwrite(FILE* f, void* address, size_t bytes);
void show(char *s, char *name);
/* Conversion utilities. */
static mode_t ar2mode(short mode);
static short mode2ar(mode_t mode);
/** Maps an AR mode to the current system mode. */
struct modemap
{
short ar_mode;
mode_t mode;
};
/** Mapping table to map an AR mode to a system mode. */
static const struct modemap armodes[MODE_COUNT] =
{
{ AR_IRUSR, S_IRUSR },
{ AR_IWUSR, S_IWUSR },
{ AR_IXUSR, S_IXUSR },
{ AR_IRGRP, S_IRGRP },
{ AR_IWGRP, S_IWGRP },
{ AR_IXGRP, S_IXGRP },
{ AR_IROTH, S_IROTH },
{ AR_IWOTH, S_IWOTH },
{ AR_IXOTH, S_IXOTH },
{ AR_ISUID, S_ISUID },
{ AR_ISGID, S_ISGID } };
/** Convert an "ar" mode to a system specific
* mode.
*/
static mode_t ar2mode(short mode)
{
int i;
mode_t result = 0;
for (i = 0; i < MODE_COUNT; i++)
{
if (mode & armodes[i].ar_mode)
{
result = result | armodes[i].mode;
}
}
return result;
}
/** Convert a system specified mode to
* an ar compatible mode.
*/
static short mode2ar(mode_t mode)
{
int i;
short result = 0;
for (i = 0; i < MODE_COUNT; i++)
{
if (mode & armodes[i].mode)
{
result = result | armodes[i].ar_mode;
}
}
return result;
}
static void usage(void)
{
error(TRUE, "usage: %s [qdprtxl][vc] archive [file] ...\n", progname);
}
/*VARARGS2*/
void error(BOOL quit, char *str1, char *str2)
{
char errbuf[256];
sprint(errbuf, str1, str2);
fwrite(errbuf, strlen(errbuf), 1, stderr);
if (quit)
{
remove(temp_arch);
exit(1);
}
}
/** Opens the specified archive. */
FILE* open_archive(char *name, int mode)
{
unsigned short magic = 0;
FILE* file = NULL;
if (mode == CREATE)
{
file = fopen(name, "wb+");
if (file == NULL)
error(TRUE, "cannot create %s\n", name);
magic = MAGIC_NUMBER;
wr_int2(file, magic);
return file;
}
file = fopen(name, "rb");
if (file == NULL)
{
/* mode APPEND and files does not exist. */
if (mode == APPEND)
{
fclose(open_archive(name, CREATE));
if (!nocr_fl)
error(FALSE, "creating %s\n", name);
return open_archive(name, APPEND);
}
error(TRUE, "cannot open %s\n", name);
}
else
/* file already exists, simply open it for appending */
{
if (mode == APPEND)
{
fclose(file);
file = fopen(name, "a+b");
}
}
fseek(file, 0, SEEK_SET);
magic = rd_unsigned2(file);
if (magic != AALMAG && magic != ARMAG)
error(TRUE, "%s is not in ar format\n", name);
return file;
}
void
catch(int param)
{
remove(temp_arch);
exit(2);
}
int main(int argc, char *argv[])
{
register char *ptr;
int needs_arg = 0;
progname = argv[0];
if (argc < 3)
usage();
for (ptr = argv[1]; *ptr; ptr++)
{
switch (*ptr)
{
case 't':
show_fl = TRUE;
break;
case 'v':
verbose = TRUE;
break;
case 'x':
ex_fl = TRUE;
break;
case 'q':
needs_arg = 1;
app_fl = TRUE;
break;
case 'c':
nocr_fl = TRUE;
break;
case 'u':
u_fl = TRUE;
break;
case 'p':
needs_arg = 1;
pr_fl = TRUE;
break;
case 'd':
needs_arg = 1;
del_fl = TRUE;
break;
case 'r':
needs_arg = 1;
rep_fl = TRUE;
break;
#ifdef DISTRIBUTION
case 'D' :
distr_fl = TRUE;
break;
#endif
default:
usage();
}
}
if (needs_arg && argc <= 3)
usage();
#ifdef DISTRIBUTION
if (distr_fl)
{
static struct stat statbuf;
stat(progname, &statbuf);
distr_time = statbuf.st_mtime;
}
#endif
if (sys_tmpnam(temp_arch) == NULL)
{
error(TRUE, "Cannot create a temporary filename\n", NULL);
}
if (app_fl + ex_fl + del_fl + rep_fl + show_fl + pr_fl != 1)
usage();
if (u_fl && !rep_fl)
usage();
if (rep_fl || del_fl
#ifdef AAL
|| app_fl
#endif
)
{
/*fclose(mkstemp(temp_arch));*/
}
#ifdef AAL
tab = (struct ranlib *) malloc(512 * sizeof(struct ranlib));
tstrtab = malloc(4096);
if (!tab || !tstrtab)
error(TRUE, "Out of core\n", NULL);
tabsz = 512;
strtabsz = 4096;
#endif
signal(SIGINT,
catch);
get(argc, argv);
return 0;
}
/* Read next member of in the archive file "f". */
MEMBER *get_member(FILE *f)
{
static MEMBER member;
again:
if (rd_arhdr(f, &member) == 0)
return NIL_MEM;
if (member.ar_size < 0)
{
error(TRUE, "archive has member with negative size\n",NULL);
}
if (equal(SYMDEF, member.ar_name))
{
fseek(f, member.ar_size, SEEK_CUR);
goto again;
}
return &member;
}
void get(int argc, char *argv[])
{
register MEMBER *member;
FILE *ar_f;
int i = 0;
char buffer[FILENAME_MAX];
size_t read_chars;
FILE* temp_fd;
ar_f = open_archive(argv[2], (show_fl || pr_fl || ex_fl) ? READ : APPEND);
if (rep_fl || del_fl
#ifdef AAL
|| app_fl
#endif
)
temp_fd = open_archive(temp_arch, CREATE);
while ((member = get_member(ar_f)) != NIL_MEM)
{
if (argc > 3)
{
for (i = 3; i < argc; i++)
{
sys_basename(argv[i],buffer);
if (equal(buffer, member->ar_name))
break;
}
if (i == argc || app_fl)
{
if (rep_fl || del_fl
#ifdef AAL
|| app_fl
#endif
)
{
#ifdef AAL
if (i != argc)
{
print("%s: already in archive\n", argv[i]);
argv[i] = "";
}
#endif
wr_arhdr(temp_fd, member);
copy_member(member, ar_f, temp_fd, FALSE);
}
else
{
#ifndef AAL
if (app_fl && i != argc)
{
print("%s: already in archive\n", argv[i]);
argv[i] = "";
}
#endif
fseek(ar_f, even(member->ar_size),SEEK_CUR);
}
continue;
}
}
if (ex_fl || pr_fl)
extract(ar_f,member);
else
{
if (rep_fl)
add(argv[i], ar_f, temp_fd, "r - %s\n");
else if (show_fl)
{
char buf[sizeof(member->ar_name) + 2];
register char *p = buf, *q = member->ar_name;
while (q <= &member->ar_name[sizeof(member->ar_name)-1] && *q)
{
*p++ = *q++;
}
*p++ = '\n';
*p = '\0';
if (verbose)
{
char *mode = get_mode(member->ar_mode);
char *date = ctime(&(member->ar_date));
*(date + 16) = '\0';
*(date + 24) = '\0';
print("%s%3u/%u%7ld %s %s %s",
mode,
(unsigned) (member->ar_uid & 0377),
(unsigned) (member->ar_gid & 0377),
member->ar_size,
date+4,
date+20,
buf);
}
else print(buf);
}
else if (del_fl)
{
show("d - %s\n", member->ar_name);
}
fseek(ar_f, even(member->ar_size), SEEK_CUR);
}
argv[i] = "";
}
if (argc > 3)
{
for (i = 3; i < argc; i++)
if (argv[i][0] != '\0')
{
#ifndef AAL
if (app_fl)
add(argv[i], ar_f, "a - %s\n");
else
#endif
if (rep_fl
#ifdef AAL
|| app_fl
#endif
)
add(argv[i], ar_f, temp_fd, "a - %s\n");
else
{
print("%s: not found\n", argv[i]);
}
}
}
if (rep_fl || del_fl
#ifdef AAL
|| app_fl
#endif
)
{
signal(SIGINT, SIG_IGN);
fclose(ar_f);
fclose(temp_fd);
ar_f = open_archive(argv[2], CREATE);
temp_fd = open_archive(temp_arch, APPEND);
#ifdef AAL
write_symdef(ar_f);
#endif
while ((read_chars = fread(io_buffer, 1, IO_SIZE, temp_fd)) > 0)
mwrite(ar_f, io_buffer, read_chars);
fclose(temp_fd);
remove(temp_arch);
}
fclose(ar_f);
}
/** Add an entry into the archive.
*
* @param[in] name path specification of the file to add.
* @param[in] ar Original ar file for update.
* @param[in] dst Archive name that will have its file added.
*
*/
void add(char *name, FILE* ar, FILE* dst, char *mess)
{
static MEMBER member;
size_t read_chars;
struct stat status;
char buffer[FILENAME_MAX];
FILE* src_fd = NULL;
if (stat(name, &status) < 0)
{
error(FALSE, "cannot find %s\n", name);
return;
}
else if (S_ISDIR(status.st_mode))
{
error(FALSE, "%s is a directory (ignored)\n", name);
return;
}
else if (u_fl && status.st_mtime <= member.ar_date)
{
wr_arhdr(dst, &member);
copy_member(&member, ar, dst, FALSE);
return;
}
else if ((src_fd = fopen(name, "rb")) == NULL)
{
error(FALSE, "cannot open %s\n", name);
return;
}
sys_basename(name, buffer);
strncpy (member.ar_name, buffer, sizeof(member.ar_name));
member.ar_uid = status.st_uid;
member.ar_gid = status.st_gid;
member.ar_mode = mode2ar(status.st_mode);
member.ar_date = status.st_mtime;
member.ar_size = status.st_size;
#ifdef DISTRIBUTION
if (distr_fl)
{
member.ar_uid = 2;
member.ar_gid = 2;
member.ar_mode = AR_IUSR | AR_IWUSR | AR_IRGRP | AR_IROTH;
member.ar_date = distr_time;
}
#endif
wr_arhdr(dst, &member);
#ifdef AAL
do_object(src_fd, member.ar_size);
fseek(src_fd, 0L, SEEK_SET);
offset += AR_TOTAL + even(member.ar_size);
#endif
while (status.st_size > 0)
{
size_t x = IO_SIZE;
read_chars = x;
if (status.st_size < x)
{
x = status.st_size;
read_chars = x;
status.st_size = 0;
x = even(x);
}
else status.st_size -= x;
if (fread(io_buffer, 1, read_chars, src_fd) != read_chars)
{
error(FALSE,"%s seems to shrink\n", name);
break;
}
mwrite(dst, io_buffer, x);
}
if (verbose)
show(mess, member.ar_name);
fclose(src_fd);
}
/** Extract an archive entry pointed to by member to
* either standard output or to a file.
*
*/
void extract(FILE* ar, MEMBER *member)
{
FILE* file = stdout;
char buf[sizeof(member->ar_name) + 1];
strncpy(buf, member->ar_name, sizeof(member->ar_name));
buf[sizeof(member->ar_name)] = 0;
if (pr_fl == FALSE)
{
file = fopen(buf, "wb");
if (file == NULL)
{
error(FALSE, "cannot create %s\n", buf);
file = NULL;
}
};
if (verbose)
{
if (pr_fl == FALSE)
show("x - %s\n", buf);
else
show("\n<%s>\n\n", buf);
}
copy_member(member, ar, file, TRUE);
if (file != NULL)
fclose(file);
if (pr_fl == FALSE)
chmod(buf, ar2mode(member->ar_mode));
}
void copy_member(MEMBER *member, FILE* from, FILE* to, BOOL extracting)
{
size_t rest;
long mem_size = member->ar_size;
BOOL is_odd = odd(mem_size) ? TRUE : FALSE;
#ifdef AAL
if (!extracting)
{
long pos = ftell(from);
do_object(from, mem_size);
offset += AR_TOTAL + even(mem_size);
fseek(from, pos, SEEK_SET);
}
#endif
do
{
rest = mem_size > (size_t) IO_SIZE ? IO_SIZE : (size_t) mem_size;
if (fread(io_buffer, 1, rest, from) != rest)
{
char buf[sizeof(member->ar_name) + 1];
strncpy(buf, member->ar_name, sizeof(member->ar_name));
buf[sizeof(member->ar_name)] = 0;
error(TRUE, "read error on %s\n", buf);
}
if (to != NULL)
mwrite(to, io_buffer, rest);
mem_size -= (long) rest;
} while (mem_size > 0L);
if (is_odd)
{
fseek(from, 1L, SEEK_CUR);
if ((to != NULL) && (extracting == FALSE))
fseek(to, 1L, SEEK_CUR);
}
}
char *get_mode(int mode)
{
static char mode_buf[11];
register int tmp = mode;
int i;
mode_buf[9] = ' ';
for (i = 0; i < 3; i++)
{
mode_buf[i * 3] = (tmp & AR_IRUSR) ? 'r' : '-';
mode_buf[i * 3 + 1] = (tmp & AR_IWUSR) ? 'w' : '-';
mode_buf[i * 3 + 2] = (tmp & AR_IXUSR) ? 'x' : '-';
tmp <<= 3;
}
if (mode & AR_ISUID)
mode_buf[2] = 's';
if (mode & AR_ISGID)
mode_buf[5] = 's';
return mode_buf;
}
void wr_fatal(void)
{
error(TRUE, "write error\n", NULL);
}
void rd_fatal(void)
{
error(TRUE, "read error\n", NULL);
}
void mwrite(FILE* f, void* address, size_t bytes)
{
if (fwrite(address, 1, bytes, f) != bytes)
error(TRUE, "write error\n", NULL);
}
void show(char *s, char *name)
{
MEMBER x;
char buf[sizeof(x.ar_name) + 1];
register char *p = buf, *q = name;
while (q <= &name[sizeof(x.ar_name) - 1] && *q)
*p++ = *q++;
*p++ = '\0';
print(s, buf);
}
#ifdef AAL
/*
* Write out the ranlib table: first 4 bytes telling how many ranlib structs
* there are, followed by the ranlib structs,
* then 4 bytes giving the size of the string table, followed by the string
* table itself.
*/
void write_symdef(FILE *ar)
{
register struct ranlib *ran;
register int i;
register long delta;
time_t time_value;
MEMBER arbuf;
if (!tnum)
return;
if (odd(tssiz))
tstrtab[tssiz++] = '\0';
for (i = 0; i < sizeof(arbuf.ar_name); i++)
arbuf.ar_name[i] = '\0';
strcpy(arbuf.ar_name, SYMDEF);
arbuf.ar_size = 4 + 2 * 4 * (long) tnum + 4 + (long) tssiz;
time(&time_value);
arbuf.ar_date = (long) time_value;
arbuf.ar_uid = getuid();
arbuf.ar_gid = getgid();
arbuf.ar_mode = AR_IRUSR | AR_IRGRP | AR_IROTH;
#ifdef DISTRIBUTION
if (distr_fl)
{
arbuf.ar_uid = 2;
arbuf.ar_gid = 2;
arbuf.ar_date = distr_time;
}
#endif
wr_arhdr(ar, &arbuf);
wr_long(ar, (long) tnum);
/*
* Account for the space occupied by the magic number
* and the ranlib table.
*/
delta = 2 + AR_TOTAL + arbuf.ar_size;
for (ran = tab; ran < &tab[tnum]; ran++)
{
ran->ran_pos += delta;
}
wr_ranlib(ar, tab, (long) tnum);
wr_long(ar, (long) tssiz);
wr_bytes(ar, tstrtab, (long) tssiz);
}
/*
* Return whether the bytes in `buf' form a good object header.
* The header is put in `headp'.
*/
int is_outhead(struct outhead *headp)
{
return !BADMAGIC(*headp) && headp->oh_nname != 0;
}
void do_object(FILE* f, long size)
{
struct outhead headbuf;
if (size < SZ_HEAD)
{
/* It can't be an object file. */
return;
}
/*
* Read a header to see if it is an object file.
*/
if (!rd_fdopen(f))
{
rd_fatal();
}
rd_ohead(&headbuf);
if (!is_outhead(&headbuf))
{
return;
}
do_names(&headbuf);
}
/*
* First skip the names and read in the string table, then seek back to the
* name table and read and write the names one by one. Update the ranlib table
* accordingly.
*/
void do_names(struct outhead *headp)
{
register char *strings = NULL;
register int nnames = headp->oh_nname;
#define NNAMES 100
struct outname namebuf[NNAMES];
long xxx = OFF_CHAR(*headp);
if ( (headp->oh_nchar != (unsigned int) headp->oh_nchar)
|| ((strings = malloc((unsigned int) headp->oh_nchar))) == NULL)
{
error(TRUE, "string table too big\n", NULL);
}
rd_string(strings, headp->oh_nchar);
while (nnames)
{
int i = nnames >= NNAMES ? NNAMES : nnames;
register struct outname *p = namebuf;
nnames -= i;
rd_name(namebuf, i);
while (i--)
{
long off = p->on_foff - xxx;
if (p->on_foff == (long) 0)
{
p++;
continue; /* An unrecognizable name. */
}
p->on_mptr = strings + off;
/*
* Only enter names that are exported and are really
* defined. Also enter common names. Note, that
* this might cause problems when the name is really
* defined in a later file, with a value != 0.
* However, this problem also exists on the Unix
* ranlib archives.
*/
if ((p->on_type & S_EXT) && (p->on_type & S_TYP) != S_UND)
enter_name(p);
p++;
}
}
free(strings);
}
void enter_name(struct outname *namep)
{
register char *cp;
if (tnum >= tabsz)
{
tab = (struct ranlib *) realloc((char *) tab,
(tabsz += 512) * sizeof(struct ranlib));
if (!tab)
error(TRUE, "Out of core\n", NULL);
}
tab[tnum].ran_off = tssiz;
tab[tnum].ran_pos = offset;
for (cp = namep->on_mptr;; cp++)
{
if (tssiz >= strtabsz)
{
tstrtab = realloc(tstrtab, (strtabsz += 4096));
if (!tstrtab)
error(TRUE, "string table overflow\n", NULL);
}
tstrtab[tssiz++] = *cp;
if (!*cp)
break;
}
tnum++;
}
#endif /* AAL */