/******************************************************************************
* This file is Copyright 1992 by Philip G. Richards. All Rights Reserved.
* See the file README that came with this distribution for permissions on
* code usage, copying, and distribution. It comes with absolutely no warranty.
* email: <pgr@prg.ox.ac.uk>
******************************************************************************/
/* ---INFOBEGIN--- * DO NOT DELETE THIS COMMENT BLOCK!!!
COMMAND tar remote "collect together remote files into a local tar file"
* ---INFOEND--- */
#include "client.h"
#include "table.h"
#include <stdlib.h>
#define TAR_BLOCKSIZE 512
#define TAR_NAMESIZE 100
typedef union {
char data[TAR_BLOCKSIZE];
struct header {
char name[TAR_NAMESIZE];
char mode[8];
char uid[8];
char gid[8];
char size[12];
char mtime[12];
char chksum[8];
char flag;
char linkname[TAR_NAMESIZE];
} h;
} TarHeader;
static int tarout;
static TarHeader tarhdr;
static unsigned int baselen, cwdlen;
static char *abscwd;
static int recursive;
static void
zero_tarhdr(void)
{
unsigned int i;
/*
** how embarassing... however, I probably can't assume the existence
** of bzero() or memset()... (I'm disheartened by this portability
** lark, you know... I wonder if I can assume the portability of
** `for'...)
*/
for (i = 0; i < sizeof(tarhdr.data); i++)
tarhdr.data[i] = 0;
}
#define sprintf_field(F,V) \
(void)sprintf(&tarhdr.h.F[0], "%0*o ", sizeof(tarhdr.h.F) - 1, (V))
#define sprintf_fieldn(F,V) \
(void)sprintf(&tarhdr.h.F[0], "%0*o ", sizeof(tarhdr.h.F) - 2, (V))
static void
addtar_chksum(void)
{
unsigned int i;
u_long chksum;
for (i = 0; i < sizeof(tarhdr.h.chksum); i++)
tarhdr.h.chksum[i] = ' ';
for (chksum = 0, i = 0; i < sizeof(tarhdr); i++)
chksum += (u_char)tarhdr.data[i];
sprintf_fieldn(chksum, chksum);
}
static int
tar_file(char *name, struct stat *sbufp, int depth)
{
char *path, *remname;
int bytes;
int retval = 0;
if (!validate_operation(name, UTIL_DOWNLOAD))
return -1;
zero_tarhdr();
(void)sprintf(&tarhdr.h.name[0], "%s", name + baselen);
sprintf_fieldn(mode, sbufp->st_mode);
sprintf_fieldn(uid, (u_short)(-2)); /* nobody */
sprintf_fieldn(gid, (u_short)(-2)); /* nogroup */
sprintf_field(size, sbufp->st_size);
sprintf_field(mtime, sbufp->st_mtime);
tarhdr.h.flag = '0'; /* regular file */
addtar_chksum();
bytes = write(tarout, (char*)&tarhdr, sizeof(tarhdr));
if (bytes < 0)
{
ffprintf(STDERR, "?tar: can't write header for file `%s': %s\n",
name + baselen, strerror(errno));
return -1;
}
else if (bytes < (int)sizeof(tarhdr))
{
ffprintf(STDERR, "?tar: only wrote partial header for `%s': %s\n",
name + baselen, strerror(errno));
ffprintf(STDERR, "?? tarfile is probably corrupt!\n");
return -1;
}
/* check if the cwd is a prefix of the remote filename */
if (baselen > cwdlen && strncmp(abscwd, name, cwdlen) == 0)
remname = name + cwdlen + 1;
else
remname = name;
if (recursive)
path = name + baselen;
else
path = strrchr(name, '/') + 1;
if (client_trace >= 2)
ffprintf(STDINFO, "downloading `%s' (%d bytes)\n",
remname, sbufp->st_size);
bytes = util_download(remname, tarout, 0, sbufp->st_size);
if (bytes < 0)
bytes = 0;
else if (bytes < sbufp->st_size)
ffprintf(STDERR, "?failed to download `%d' bytes -- adding padding\n",
sbufp->st_size - bytes);
retval = (bytes < sbufp->st_size);
bytes = (sbufp->st_size - bytes)
+ (TAR_BLOCKSIZE - (sbufp->st_size % TAR_BLOCKSIZE))
% TAR_BLOCKSIZE;
zero_tarhdr();
for ( ; bytes > 0; bytes -= TAR_BLOCKSIZE)
{
unsigned int towrite;
int written;
towrite = (bytes < TAR_BLOCKSIZE)? bytes: TAR_BLOCKSIZE;
written = write(tarout, (char*)&tarhdr, towrite);
retval |= (written < (int)towrite);
if (written < 0)
{
ffprintf(STDERR, "?tar: failed to pad tarfile for file `%s': %s\n",
name, strerror(errno));
ffprintf(STDERR, "?? tarfile is probably corrupt!\n");
break;
}
}
return -retval;
}
static int
do_tar(char *name)
{
char *dirname;
if (!validate_operation(name, UTIL_PROCESS_FILE))
return -1;
dirname = util_abs_path(name);
/* util_abs_path() is guaranteed to return path with a '/' */
baselen = strrchr(dirname, '/') - dirname + 1;
(void)free(dirname);
return util_process_file(name, recursive, 0, tar_file, 0, 0);
}
/* ARGSUSED */
int
rtar_main(int argc, char *const*argv, char **envp)
{
int retval, errcnt, ch;
recursive = 0;
optind = 0;
opterr = 0;
errcnt = 0;
#ifdef HAVE_OPTRESET
optreset = 1;
optind = 1;
#endif
while ((ch = getopt(argc, argv, "r")) != EOF)
switch (ch)
{
case 'r':
recursive = MAXRECURSION;
break;
default:
errcnt++;
break;
}
if (errcnt > 0 || argc - optind < 2)
{
ffprintf(STDERR, "?tar usage: tar [-r] [-|tarfile] file [file...]\n");
return 1;
}
if (argv[optind][0] == '-' && argv[optind][1] == '\0')
tarout = 1; /* write to standard out */
else
{
tarout = open(argv[optind], O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (tarout < 0)
{
ffprintf(STDERR, "?can't open tarfile `%s' for writing: %s\n",
argv[optind], strerror(errno));
return 1;
}
}
abscwd = util_abs_path(env_dir);
cwdlen = strlen(abscwd);
/* special case "/" */
if (cwdlen == 1)
cwdlen = 0;
retval = -util_process_arglist(argv + optind + 1, do_tar);
(void)free(abscwd);
/* write trailing 2 null blocks */
zero_tarhdr();
(void)write(tarout, (char*)&tarhdr, sizeof(tarhdr));
(void)write(tarout, (char*)&tarhdr, sizeof(tarhdr));
if (tarout != 1)
(void)close(tarout);
client_done();
return retval;
}
syntax highlighted by Code2HTML, v. 0.9.1