home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Dream 52
/
Amiga_Dream_52.iso
/
Amiga
/
Workbench
/
Archivers
/
PPCDeTarWOS.lha
/
PPCDeTar.c
< prev
next >
Wrap
C/C++ Source or Header
|
1998-02-18
|
11KB
|
521 lines
/*
** $VER: DeTar.c 1.5 (15.2.98)
**
** DeTar main module
**
** written in 1995-98 by Andreas R. Kleinert, based on code by Steve Sampson
** Public Domain. (See DeTar.doc)
** Modified by Steffen P. HΣuser to use WarpOS instead of - ELF sucks -
** ppc.library
*/
/* ------------------------------------------------------------------------------ */
/*
* detar.c
*
* Version 1.1
*
* Fixed bug in convert() wich produced errors on files without
* a slash '/' in them.
*
* Unix Tape Archive (TAR) Extractor for MSDOS
*
* Public Domain January 1988, Steve Sampson
* Based on the public domain TAR program by John Gilmore
*
* Compiled with ECO-C88 V3.21
*
*/
/* Includes */
#define __USE_SYSBASE
#include <exec/types.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <time.h>
#include <clib/exec_protos.h>
#include <clib/dos_protos.h>
#include "detar.h"
/* Defines */
#define isodigit(c) ( ((c) >= '0') && ((c) <= '7') )
/* Globals */
FILE *fdi,
*fdo;
union record *head; /* the archive file header */
struct stat hstat; /* the decoded stat info */
struct {
long year;
long month;
long day;
long hour;
long min;
long sec;
} tm;
long ugswidth = 11;
long dir_only = 0;
char tempname[256];
char version [] = "\0$VER: detar 1.5 (15.2.98)";
/* Forward References, Prototypes */
void dir_tar(void);
void de_tar(void);
void decode_header(union record *header, struct stat *st, long *stdp);
void demode(unsigned mode, char *string);
long from_oct(long digs, char *where);
/* Main Program */
void main(long argc, char **argv)
{
char *p;
long retval = 0;
long add = FALSE, valid = FALSE;
if(!argc) exit(20);
/* The operator can ask for directory only or de-tar only. */
argv++;
if(argc == 3 && **argv == 'd')
{
dir_only++;
argc--;
argv++;
}
if (argc != 2)
{
fprintf(stderr, &version[7]);
fprintf(stderr, "\nWarpOS PPC Port by Steffen Haeuser in 1998. Public Domain."
"\nAmiga Port by Andreas R. Kleinert in 1995-98. Public Domain."
"\nPublic Domain January 1988, Steve Sampson"
"\nBased on the public domain TAR program by John Gilmore");
fprintf(stderr, "\nUsage: detar [d] filename[.tar]");
fprintf(stderr, "\nWhere d means 'list content only'\n");
exit(1);
}
for (p = *argv; *p; p++) *p = (char)toupper(*p);
/* parse the many possibilities, this stuff not needed with MSC */
if ((p = strrchr(*argv, '.')) == (char *)NULL) { add = valid = TRUE; }
else if (strcmp(p, ".TAR") == NULL) { strcpy(tempname, *argv); valid = TRUE; }
else if (*(p+1) == '/' || *(p+1) == '\\') { add = valid = TRUE; valid = TRUE; }
if(valid)
{
if(add)
{
strcpy(tempname, *argv);
strcat(tempname, ".TAR");
}
fdi = fopen(tempname, "rb");
if(fdi)
{
setvbuf(fdi, NULL, _IOFBF, 8192);
head = (union record *)malloc(512);
if(head)
{
if (dir_only) dir_tar();
else de_tar();
free(head);
}else
{
fprintf(stderr, "Not enough memory !\n");
retval = 20;
}
fclose(fdi);
}else
{
fprintf(stderr, "Tar file '%s' not found\n", tempname);
retval = 20;
}
}else
{
fprintf(stderr, "File '%s' not a TAR archive\n", *argv);
retval = 20;
}
exit(retval);
}
/*
* Produce a directory of the files contained on the TAR
*/
void dir_tar(void)
{
char modes[11], *timestamp;
char uform[11], gform[11];
char *user, *group, size[24];
long n, pad, header_std;
time_t timeval;
for (;;)
{
if ((n = fread((char *)head, 1, 512, fdi)) == EOF)
{
return;
}
decode_header(head, &hstat, &header_std);
/* File type and modes */
modes[0] = '?';
switch (head->header.linkflag)
{
case LF_NORMAL:
case LF_OLDNORMAL:
case LF_LINK:
{
modes[0] = '-';
if ('/' == head->header.name[strlen(head->header.name)-1])
modes[0] = 'd';
break;
}
case LF_DIR:
{
modes[0] = 'd';
break;
}
case LF_SYMLINK:
{
modes[0] = 'l';
break;
}
case LF_BLK:
{
modes[0] = 'b';
break;
}
case LF_CHR:
{
modes[0] = 'c';
break;
}
case LF_FIFO:
{
modes[0] = 'f';
break;
}
case LF_CONTIG:
{
modes[0] = '=';
break;
}
}
demode(hstat.st_mode, modes+1);
/* Timestamp */
timeval = hstat.st_mtime;
timestamp = ctime(&timeval);
timestamp[16] = '\0';
timestamp[24] = '\0';
/* User and group names */
if (*head->header.uname && header_std)
{
user = head->header.uname;
}else
{
user = uform;
sprintf(uform, "%ld", (long)hstat.st_uid);
}
if (*head->header.gname && header_std)
{
group = head->header.gname;
}else
{
group = gform;
sprintf(gform, "%ld", (long)hstat.st_gid);
}
/* Format the file size or major/minor device numbers */
switch (head->header.linkflag)
{
case LF_CHR:
case LF_BLK:
{
sprintf(size, "%ld, %ld",
(long)from_oct(8, head->header.devmajor),
(long)from_oct(8, head->header.devminor));
break;
}
default:
{
sprintf(size, "%ld", hstat.st_size);
break;
}
}
/* Figure out padding and print the whole line. */
pad = strlen(user) + strlen(group) + strlen(size) + 1;
if (pad > ugswidth) ugswidth = pad;
printf("%s %s/%s %*s%s %s %s %.*s",
modes,
user,
group,
ugswidth - pad,
"",
size,
timestamp+4, timestamp+20,
sizeof(head->header.name),
head->header.name);
switch (head->header.linkflag)
{
case LF_SYMLINK:
{
printf(" -> %s\n", head->header.linkname);
break;
}
case LF_LINK:
{
printf(" link to %s\n", head->header.linkname);
break;
}
default:
{
printf(" unknown file type '%c'\n", head->header.linkflag);
break;
}
case LF_OLDNORMAL:
case LF_NORMAL:
case LF_CHR:
case LF_BLK:
case LF_DIR:
case LF_FIFO:
case LF_CONTIG:
{
putc('\n', stdout);
break;
}
}
/* Seek to next file */
fseek(fdi, hstat.st_size, SEEK_CUR);
/* File starts on 512 byte boundary */
fseek(fdi, 512L - (hstat.st_size % 512L), SEEK_CUR);
}
}
/*
* Extract the files from the TAR archive
*
*/
void de_tar(void)
{
ULONG size, filesize;
long header_std, c, len;
char *workfile;
for (;!feof(fdi);)
{
if ( fread((char *)head, 1, 512, fdi) == (unsigned int) EOF) break;
decode_header(head, &hstat, &header_std);
workfile = head->header.name;
size = filesize = hstat.st_size;
len = strlen(workfile);
if(len > 0)
{
if( (workfile[len-1] == '/')
|| (workfile[len-1] == ':') )
{
BPTR lock;
printf("Creating: %s ...", workfile);
workfile[len-1] = (char) 0;
lock = CreateDir(workfile);
if(lock)
{
printf(" OK\n");
UnLock(lock);
}else printf(" Failed\n");
fseek(fdi, filesize, SEEK_CUR);
continue;
}
printf("Extracting: %s\n", workfile);
fdo = fopen(workfile, "wb");
if(fdo)
{
setvbuf(fdo, NULL, _IOFBF, 8192);
while ((c = getc(fdi)) != EOF)
{
putc(c, fdo); size--;
if(!size) /* Get to next 512 byte boundary */
{
fseek(fdi, 512L - (filesize % 512L), SEEK_CUR);
break;
}
}
fclose(fdo);
}else printf(" Failed\n");
}else printf("Skipping empty entry\n");
}
}
/*
* Break down the header info into stat info
*
* Set "*stdp" to != 0 or == 0 depending whether
* header record is "Unix Standard" or "old" tar format.
*
*/
void decode_header(union record *header, struct stat *st, long *stdp)
{
st->st_mode = (long)from_oct( 8, header->header.mode);
st->st_mtime = from_oct(12, header->header.mtime);
st->st_size = from_oct(12, header->header.size);
if (0 == strcmp(header->header.magic, TMAGIC)) {
/* Unix Standard tar archive */
*stdp = 1;
st->st_dev = 0;
} else {
/* Old fashioned tar archive */
*stdp = 0;
st->st_uid = (long)from_oct(8, header->header.uid);
st->st_gid = (long)from_oct(8, header->header.gid);
st->st_dev = 0;
}
}
/*
* Decode the mode string from a stat entry into a 9-char string
*/
void demode(unsigned mode, char *string)
{
register unsigned mask;
register char *rwx = "rwxrwxrwx";
for (mask = 0400; mask != 0; mask >>= 1) {
if (mode & mask)
*string++ = *rwx++;
else {
*string++ = '-';
rwx++;
}
}
if (mode & S_ISUID)
if (string[-7] == 'x')
string[-7] = 's';
else
string[-7] = 'S';
if (mode & S_ISGID)
if (string[-4] == 'x')
string[-4] = 's';
else
string[-4] = 'S';
if (mode & S_ISVTX)
if (string[-1] == 'x')
string[-1] = 't';
else
string[-1] = 'T';
*string = '\0';
}
/*
* Quick and dirty octal conversion.
*
* Result is -1 if the field is invalid (all blank, or nonoctal).
*/
long from_oct(long digs, char *where)
{
register long value;
while (isspace(*where)) { /* Skip spaces */
where++;
if (--digs <= 0)
return -1; /* All blank field */
}
value = 0;
while (digs > 0 && isodigit(*where)) { /* Scan til nonoctal */
value = (value << 3) | (*where++ - '0');
--digs;
}
if (digs > 0 && *where && !isspace(*where))
return -1; /* Ended on non-space/nul */
return value;
}