/*
* untgz . c - - Display contents and extract files from a gzip ' d TAR file
*
* written by Pedro A . Aranda Gutierrez < paag @ tid . es >
* adaptation to Unix by Jean - loup Gailly < jloup @ gzip . org >
* various fixes by Cosmin Truta < cosmint @ cs . ubbcluj . ro >
*
* This software is provided ' as - is ' , without any express or implied
* warranty . In no event will the authors be held liable for any damages
* arising from the use of this software .
*
* Permission is granted to anyone to use this software for any purpose ,
* including commercial applications , and to alter it and redistribute it
* freely , subject to the following restrictions :
*
* 1 . The origin of this software must not be misrepresented ; you must not
* claim that you wrote the original software . If you use this software
* in a product , an acknowledgment in the product documentation would be
* appreciated but is not required .
* 2 . Altered source versions must be plainly marked as such , and must not be
* misrepresented as being the original software .
* 3 . This notice may not be removed or altered from any source distribution .
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include "zlib.h"
#ifdef _WIN32
# include <direct.h>
# include <io.h>
# include <windows.h>
# ifndef F_OK
# define F_OK 0
# endif
# define mkdir(dirname,mode) _mkdir(dirname)
# ifdef _MSC_VER
# define access(path,mode) _access(path,mode)
# define chmod(path,mode) _chmod(path,mode)
# define strdup(str) _strdup(str)
# endif
#else
# include <sys/stat.h>
# include <unistd.h>
# include <utime.h>
#endif
/* values used in typeflag field */
#define REGTYPE '0' /* regular file */
#define AREGTYPE '\0' /* regular file */
#define LNKTYPE '1' /* link */
#define SYMTYPE '2' /* reserved */
#define CHRTYPE '3' /* character special */
#define BLKTYPE '4' /* block special */
#define DIRTYPE '5' /* directory */
#define FIFOTYPE '6' /* FIFO special */
#define CONTTYPE '7' /* reserved */
/* GNU tar extensions */
#define GNUTYPE_DUMPDIR 'D' /* file names from dumped directory */
#define GNUTYPE_LONGLINK 'K' /* long link name */
#define GNUTYPE_LONGNAME 'L' /* long file name */
#define GNUTYPE_MULTIVOL 'M' /* continuation of file from another volume */
#define GNUTYPE_NAMES 'N' /* file name that does not fit into main hdr */
#define GNUTYPE_SPARSE 'S' /* sparse file */
#define GNUTYPE_VOLHDR 'V' /* tape/volume header */
/* tar header */
#define BLOCKSIZE 512
#define SHORTNAMESIZE 100
struct tar_header
{ /* byte offset */
char name[100 ]; /* 0 */
char mode[8 ]; /* 100 */
char uid[8 ]; /* 108 */
char gid[8 ]; /* 116 */
char size[12 ]; /* 124 */
char mtime[12 ]; /* 136 */
char chksum[8 ]; /* 148 */
char typeflag; /* 156 */
char linkname[100 ]; /* 157 */
char magic[6 ]; /* 257 */
char version[2 ]; /* 263 */
char uname[32 ]; /* 265 */
char gname[32 ]; /* 297 */
char devmajor[8 ]; /* 329 */
char devminor[8 ]; /* 337 */
char prefix[155 ]; /* 345 */
/* 500 */
};
union tar_buffer
{
char buffer[BLOCKSIZE];
struct tar_header header;
};
struct attr_item
{
struct attr_item *next;
char *fname;
int mode;
time_t time;
};
enum { TGZ_EXTRACT, TGZ_LIST, TGZ_INVALID };
char *prog;
void error(const char *msg)
{
fprintf(stderr, "%s: %s\n" , prog, msg);
exit (1 );
}
const char *TGZsuffix[] = { "\0" , ".tar" , ".tar.gz" , ".taz" , ".tgz" , NULL };
/* return the file name of the TGZ archive */
/* or NULL if it does not exist */
char *TGZfname (const char *arcname)
{
static char buffer[1024 ];
int origlen,i;
strcpy(buffer,arcname);
origlen = strlen(buffer);
for (i=0 ; TGZsuffix[i]; i++)
{
strcpy(buffer+origlen,TGZsuffix[i]);
if (access(buffer,F_OK) == 0 )
return buffer;
}
return NULL;
}
/* error message for the filename */
void TGZnotfound (const char *arcname)
{
int i;
fprintf(stderr,"%s: Couldn't find " ,prog);
for (i=0 ;TGZsuffix[i];i++)
fprintf(stderr,(TGZsuffix[i+1 ]) ? "%s%s, " : "or %s%s\n" ,
arcname,
TGZsuffix[i]);
exit (1 );
}
/* convert octal digits to int */
/* on error return -1 */
int getoct (char *p,int width)
{
int result = 0 ;
char c;
while (width--)
{
c = *p++;
if (c == 0 )
break ;
if (c == ' ' )
continue ;
if (c < '0' || c > '7' )
return -1 ;
result = result * 8 + (c - '0' );
}
return result;
}
/* convert time_t to string */
/* use the "YYYY/MM/DD hh:mm:ss" format */
char *strtime (time_t *t)
{
struct tm *local;
static char result[32 ];
local = localtime(t);
sprintf(result,"%4d/%02d/%02d %02d:%02d:%02d" ,
local->tm_year+1900 , local->tm_mon+1 , local->tm_mday,
local->tm_hour, local->tm_min, local->tm_sec);
return result;
}
/* set file time */
int setfiletime (char *fname,time_t ftime)
{
#ifdef _WIN32
static int isWinNT = -1 ;
SYSTEMTIME st;
FILETIME locft, modft;
struct tm *loctm;
HANDLE hFile;
int result;
loctm = localtime(&ftime);
if (loctm == NULL)
return -1 ;
st.wYear = (WORD)loctm->tm_year + 1900 ;
st.wMonth = (WORD)loctm->tm_mon + 1 ;
st.wDayOfWeek = (WORD)loctm->tm_wday;
st.wDay = (WORD)loctm->tm_mday;
st.wHour = (WORD)loctm->tm_hour;
st.wMinute = (WORD)loctm->tm_min;
st.wSecond = (WORD)loctm->tm_sec;
st.wMilliseconds = 0 ;
if (!SystemTimeToFileTime(&st, &locft) ||
!LocalFileTimeToFileTime(&locft, &modft))
return -1 ;
if (isWinNT < 0 )
isWinNT = (GetVersion() < 0 x80000000) ? 1 : 0 ;
hFile = CreateFile(fname, GENERIC_WRITE, 0 , NULL, OPEN_EXISTING,
(isWinNT ? FILE_FLAG_BACKUP_SEMANTICS : 0 ),
NULL);
if (hFile == INVALID_HANDLE_VALUE)
return -1 ;
result = SetFileTime(hFile, NULL, NULL, &modft) ? 0 : -1 ;
CloseHandle(hFile);
return result;
#else
struct utimbuf settime;
settime.actime = settime.modtime = ftime;
return utime(fname,&settime);
#endif
}
/* push file attributes */
void push_attr(struct attr_item **list,char *fname,int mode,time_t time)
{
struct attr_item *item;
item = (struct attr_item *)malloc(sizeof (struct attr_item));
if (item == NULL)
error("Out of memory" );
item->fname = strdup(fname);
item->mode = mode;
item->time = time;
item->next = *list;
*list = item;
}
/* restore file attributes */
void restore_attr(struct attr_item **list)
{
struct attr_item *item, *prev;
for (item = *list; item != NULL; )
{
setfiletime(item->fname,item->time);
chmod(item->fname,item->mode);
prev = item;
item = item->next;
free(prev);
}
*list = NULL;
}
/* match regular expression */
#define ISSPECIAL(c) (((c) == '*' ) || ((c) == '/' ))
int ExprMatch (char *string,char *expr)
{
while (1 )
{
if (ISSPECIAL(*expr))
{
if (*expr == '/' )
{
if (*string != '\\' && *string != '/' )
return 0 ;
string ++; expr++;
}
else if (*expr == '*' )
{
if (*expr ++ == 0 )
return 1 ;
while (*++string != *expr)
if (*string == 0 )
return 0 ;
}
}
else
{
if (*string != *expr)
return 0 ;
if (*expr++ == 0 )
return 1 ;
string++;
}
}
}
/* recursive mkdir */
/* abort on ENOENT; ignore other errors like "directory already exists" */
/* return 1 if OK */
/* 0 on error */
int makedir (char *newdir)
{
char *buffer = strdup(newdir);
char *p;
int len = strlen(buffer);
if (len <= 0 ) {
free(buffer);
return 0 ;
}
if (buffer[len-1 ] == '/' ) {
buffer[len-1 ] = '\0' ;
}
if (mkdir(buffer, 0755 ) == 0 )
{
free(buffer);
return 1 ;
}
p = buffer+1 ;
while (1 )
{
char hold;
while (*p && *p != '\\' && *p != '/' )
p++;
hold = *p;
*p = 0 ;
if ((mkdir(buffer, 0755 ) == -1 ) && (errno == ENOENT))
{
fprintf(stderr,"%s: Couldn't create directory %s\n" ,prog,buffer);
free(buffer);
return 0 ;
}
if (hold == 0 )
break ;
*p++ = hold;
}
free(buffer);
return 1 ;
}
int matchname (int arg,int argc,char **argv,char *fname)
{
if (arg == argc) /* no arguments given (untgz tgzarchive) */
return 1 ;
while (arg < argc)
if (ExprMatch(fname,argv[arg++]))
return 1 ;
return 0 ; /* ignore this for the moment being */
}
/* tar file list or extract */
int tar (gzFile in,int action,int arg,int argc,char **argv)
{
union tar_buffer buffer;
int len;
int err;
int getheader = 1 ;
int remaining = 0 ;
FILE *outfile = NULL;
char fname[BLOCKSIZE];
int tarmode;
time_t tartime;
struct attr_item *attributes = NULL;
if (action == TGZ_LIST)
printf(" date time size file\n"
" ---------- -------- --------- -------------------------------------\n" );
while (1 )
{
len = gzread(in, &buffer, BLOCKSIZE);
if (len < 0 )
error(gzerror(in, &err));
/*
* Always expect complete blocks to process
* the tar information .
*/
if (len != BLOCKSIZE)
{
action = TGZ_INVALID; /* force error exit */
remaining = 0 ; /* force I/O cleanup */
}
/*
* If we have to get a tar header
*/
if (getheader >= 1 )
{
/*
* if we met the end of the tar
* or the end - of - tar block ,
* we are done
*/
if (len == 0 || buffer.header.name[0 ] == 0 )
break ;
tarmode = getoct(buffer.header.mode,8 );
tartime = (time_t)getoct(buffer.header.mtime,12 );
if (tarmode == -1 || tartime == (time_t)-1 )
{
buffer.header.name[0 ] = 0 ;
action = TGZ_INVALID;
}
if (getheader == 1 )
{
strncpy(fname,buffer.header.name,SHORTNAMESIZE);
if (fname[SHORTNAMESIZE-1 ] != 0 )
fname[SHORTNAMESIZE] = 0 ;
}
else
{
/*
* The file name is longer than SHORTNAMESIZE
*/
if (strncmp(fname,buffer.header.name,SHORTNAMESIZE-1 ) != 0 )
error("bad long name" );
getheader = 1 ;
}
/*
* Act according to the type flag
*/
switch (buffer.header.typeflag)
{
case DIRTYPE:
if (action == TGZ_LIST)
printf(" %s <dir> %s\n" ,strtime(&tartime),fname);
if (action == TGZ_EXTRACT)
{
makedir(fname);
push_attr(&attributes,fname,tarmode,tartime);
}
break ;
case REGTYPE:
case AREGTYPE:
remaining = getoct(buffer.header.size,12 );
if (remaining == -1 )
{
action = TGZ_INVALID;
break ;
}
if (action == TGZ_LIST)
printf(" %s %9d %s\n" ,strtime(&tartime),remaining,fname);
else if (action == TGZ_EXTRACT)
{
if (matchname(arg,argc,argv,fname))
{
outfile = fopen(fname,"wb" );
if (outfile == NULL) {
/* try creating directory */
char *p = strrchr(fname, '/' );
if (p != NULL) {
*p = '\0' ;
makedir(fname);
*p = '/' ;
outfile = fopen(fname,"wb" );
}
}
if (outfile != NULL)
printf("Extracting %s\n" ,fname);
else
fprintf(stderr, "%s: Couldn't create %s" ,prog,fname);
}
else
outfile = NULL;
}
getheader = 0 ;
break ;
case GNUTYPE_LONGLINK:
case GNUTYPE_LONGNAME:
remaining = getoct(buffer.header.size,12 );
if (remaining < 0 || remaining >= BLOCKSIZE)
{
action = TGZ_INVALID;
break ;
}
len = gzread(in, fname, BLOCKSIZE);
if (len < 0 )
error(gzerror(in, &err));
if (fname[BLOCKSIZE-1 ] != 0 || (int )strlen(fname) > remaining)
{
action = TGZ_INVALID;
break ;
}
getheader = 2 ;
break ;
default :
if (action == TGZ_LIST)
printf(" %s <---> %s\n" ,strtime(&tartime),fname);
break ;
}
}
else
{
unsigned int bytes = (remaining > BLOCKSIZE) ? BLOCKSIZE : remaining;
if (outfile != NULL)
{
if (fwrite(&buffer,sizeof (char ),bytes,outfile) != bytes)
{
fprintf(stderr,
"%s: Error writing %s -- skipping\n" ,prog,fname);
fclose(outfile);
outfile = NULL;
remove(fname);
}
}
remaining -= bytes;
}
if (remaining == 0 )
{
getheader = 1 ;
if (outfile != NULL)
{
fclose(outfile);
outfile = NULL;
if (action != TGZ_INVALID)
push_attr(&attributes,fname,tarmode,tartime);
}
}
/*
* Abandon if errors are found
*/
if (action == TGZ_INVALID)
{
error("broken archive" );
break ;
}
}
/*
* Restore file modes and time stamps
*/
restore_attr(&attributes);
if (gzclose(in) != Z_OK)
error("failed gzclose" );
return 0 ;
}
/* ============================================================ */
void help(int exitval)
{
printf("untgz version 0.2.1\n"
" using zlib version %s\n\n" ,
zlibVersion());
printf("Usage: untgz file.tgz extract all files\n"
" untgz file.tgz fname ... extract selected files\n"
" untgz -l file.tgz list archive contents\n"
" untgz -h display this help\n" );
exit (exitval);
}
/* ============================================================ */
#if defined (WIN32) && defined (__GNUC__)
int _CRT_glob = 0 ; /* disable argument globbing in MinGW */
#endif
int main(int argc,char **argv)
{
int action = TGZ_EXTRACT;
int arg = 1 ;
char *TGZfile;
gzFile f;
prog = strrchr(argv[0 ],'\\' );
if (prog == NULL)
{
prog = strrchr(argv[0 ],'/' );
if (prog == NULL)
{
prog = strrchr(argv[0 ],':' );
if (prog == NULL)
prog = argv[0 ];
else
prog++;
}
else
prog++;
}
else
prog++;
if (argc == 1 )
help(0 );
if (strcmp(argv[arg],"-l" ) == 0 )
{
action = TGZ_LIST;
if (argc == ++arg)
help(0 );
}
else if (strcmp(argv[arg],"-h" ) == 0 )
{
help(0 );
}
if ((TGZfile = TGZfname(argv[arg])) == NULL)
TGZnotfound(argv[arg]);
++arg;
if ((action == TGZ_LIST) && (arg != argc))
help(1 );
/*
* Process the TGZ file
*/
switch (action)
{
case TGZ_LIST:
case TGZ_EXTRACT:
f = gzopen(TGZfile,"rb" );
if (f == NULL)
{
fprintf(stderr,"%s: Couldn't gzopen %s\n" ,prog,TGZfile);
return 1 ;
}
exit (tar(f, action, arg, argc, argv));
break ;
default :
error("Unknown option" );
exit (1 );
}
return 0 ;
}
Messung V0.5 in Prozent C=89 H=91 G=89
¤ Dauer der Verarbeitung: 0.14 Sekunden
(vorverarbeitet am 2026-06-17)
¤
*© Formatika GbR, Deutschland