923 lines
		
	
	
	
		
			17 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			923 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 */
 |