pidgin/pidgin
Clone
Summary
Browse
Changes
Graph
Use Meson summary() function.
2021-07-27, Elliott Sales de Andrade
cb640ea0f315
Use Meson summary() function.
Now that we require at least 0.52, we can use Meson's builtin summary printing to display the results of configuration.
Testing Done:
Configured with defaults, and with pixmaps disabled to trigger the warning: https://asciinema.org/a/mV2oxOoVCJNdmrPwgqqUJ3mkU?t=17
Reviewed at https://reviews.imfreedom.org/r/848/
/* untar.c */
/*#define VERSION "1.4"*/
/* DESCRIPTION:
* Untar extracts files from an uncompressed tar archive, or one which
* has been compressed with gzip. Usually such archives will have file
* names that end with ".tar" or ".tgz" respectively, although untar
* doesn't depend on any naming conventions. For a summary of the
* command-line options, run untar with no arguments.
*
* HOW TO COMPILE:
* Untar doesn't require any special libraries or compile-time flags.
* A simple "cc untar.c -o untar" (or the local equivalent) is
* sufficient. Even "make untar" works, without needing a Makefile.
* For Microsoft Visual C++, the command is "cl /D_WEAK_POSIX untar.c"
* (for 32 bit compilers) or "cl /F 1400 untar.c" (for 16-bit).
*
* IF YOU SEE COMPILER WARNINGS, THAT'S NORMAL; you can ignore them.
* Most of the warnings could be eliminated by adding #include <string.h>
* but that isn't portable -- some systems require <strings.h> and
* <malloc.h>, for example. Because <string.h> isn't quite portable,
* and isn't really necessary in the context of this program, it isn't
* included.
*
* PORTABILITY:
* Untar only requires the <stdio.h> header. It uses old-style function
* definitions. It opens all files in binary mode. Taken together,
* this means that untar should compile & run on just about anything.
*
* If your system supports the POSIX chmod(2), utime(2), link(2), and
* symlink(2) calls, then you may wish to compile with -D_POSIX_SOURCE,
* which will enable untar to use those system calls to restore the
* timestamp and permissions of the extracted files, and restore links.
* (For Linux, _POSIX_SOURCE is always defined.)
*
* For systems which support some POSIX features but not enough to support
* -D_POSIX_SOURCE, you might be able to use -D_WEAK_POSIX. This allows
* untar to restore time stamps and file permissions, but not links.
* This should work for Microsoft systems, and hopefully others as well.
*
* AUTHOR & COPYRIGHT INFO:
* Written by Steve Kirkendall, kirkenda@cs.pdx.edu
* Placed in public domain, 6 October 1995
*
* Portions derived from inflate.c -- Not copyrighted 1992 by Mark Adler
* version c10p1, 10 January 1993
*
* Altered by Herman Bloggs <hermanator12002@yahoo.com>
* April 4, 2003
* Changes: Stripped out gz compression code, added better interface for
* untar.
*/
#include
<windows.h>
#include
<stdio.h>
#include
<io.h>
#include
<string.h>
#include
<stdlib.h>
#ifndef SEEK_SET
# define SEEK_SET 0
#endif
#ifdef _WEAK_POSIX
# ifndef _POSIX_SOURCE
# define _POSIX_SOURCE
# endif
#endif
#ifdef _POSIX_SOURCE
#
include
<sys/types.h>
#
include
<sys/stat.h>
#
include
<sys/utime.h>
# ifdef _WEAK_POSIX
# define mode_t int
# else
#
include
<unistd.h>
# endif
#endif
#include
<glib.h>
#include
<glib/gstdio.h>
#include
<purple.h>
#include
"untar.h"
#define WSIZE 32768
/* size of decompression buffer */
#define TSIZE 512
/* size of a "tape" block */
#define CR 13
/* carriage-return character */
#define LF 10
/* line-feed character */
typedef
unsigned
char
Uchar_t
;
typedef
unsigned
short
Ushort_t
;
typedef
unsigned
long
Ulong_t
;
typedef
struct
{
char
filename
[
100
];
/* 0 name of next file */
char
mode
[
8
];
/* 100 Permissions and type (octal digits) */
char
owner
[
8
];
/* 108 Owner ID (ignored) */
char
group
[
8
];
/* 116 Group ID (ignored) */
char
size
[
12
];
/* 124 Bytes in file (octal digits) */
char
mtime
[
12
];
/* 136 Modification time stamp (octal digits)*/
char
checksum
[
8
];
/* 148 Header checksum (ignored) */
char
type
;
/* 156 File type (see below) */
char
linkto
[
100
];
/* 157 Linked-to name */
char
brand
[
8
];
/* 257 Identifies tar version (ignored) */
char
ownername
[
32
];
/* 265 Name of owner (ignored) */
char
groupname
[
32
];
/* 297 Name of group (ignored) */
char
devmajor
[
8
];
/* 329 Device major number (ignored) */
char
defminor
[
8
];
/* 337 Device minor number (ignored) */
char
prefix
[
155
];
/* 345 Prefix of name (optional) */
char
RESERVED
[
12
];
/* 500 Pad header size to 512 bytes */
}
tar_t
;
#define ISREGULAR(hdr) ((hdr).type < '1' || (hdr).type > '6')
Uchar_t
slide
[
WSIZE
];
static
const
char
*
inname
=
NULL
;
/* name of input archive */
static
FILE
*
infp
=
NULL
;
/* input byte stream */
static
FILE
*
outfp
=
NULL
;
/* output stream, for file currently being extracted */
static
Ulong_t
outsize
=
0
;
/* number of bytes remainin in file currently being extracted */
static
int
didabs
=
0
;
/* were any filenames affected by the absence of -p? */
static
untar_opt
untarops
=
0
;
/* Untar options */
/* Options checked during untar process */
#define LISTING (untarops & UNTAR_LISTING)
/* 1 if listing, 0 if extracting */
#define QUIET (untarops & UNTAR_QUIET)
/* 1 to write nothing to stdout, 0 for normal chatter */
#define VERBOSE (untarops & UNTAR_VERBOSE)
/* 1 to write extra information to stdout */
#define FORCE (untarops & UNTAR_FORCE)
/* 1 to overwrite existing files, 0 to skip them */
#define ABSPATH (untarops & UNTAR_ABSPATH)
/* 1 to allow leading '/', 0 to strip leading '/' */
#define CONVERT (untarops & UNTAR_CONVERT)
/* 1 to convert newlines, 0 to leave unchanged */
/*----------------------------------------------------------------------------*/
/* create a file for writing. If necessary, create the directories leading up
* to that file as well.
*/
static
FILE
*
createpath
(
name
)
char
*
name
;
/* pathname of file to create */
{
FILE
*
fp
;
int
i
;
/* if we aren't allowed to overwrite and this file exists, return NULL */
if
(
!
FORCE
&&
access
(
name
,
0
)
==
0
)
{
purple_debug_warning
(
"untar"
,
"%s: exists, will not overwrite without
\"
FORCE option
\"
"
,
name
);
return
NULL
;
}
/* first try creating it the easy way */
fp
=
g_fopen
(
name
,
CONVERT
?
"w"
:
"wb"
);
if
(
fp
)
return
fp
;
/* Else try making all of its directories, and then try creating
* the file again.
*/
for
(
i
=
0
;
name
[
i
];
i
++
)
{
/* If this is a slash, then temporarily replace the '/'
* with a '\0' and do a mkdir() on the resulting string.
* Ignore errors for now.
*/
if
(
name
[
i
]
==
'/'
)
{
name
[
i
]
=
'\0'
;
(
void
)
g_mkdir
(
name
,
0777
);
name
[
i
]
=
'/'
;
}
}
fp
=
g_fopen
(
name
,
CONVERT
?
"w"
:
"wb"
);
if
(
!
fp
)
purple_debug_error
(
"untar"
,
"Error opening: %s"
,
name
);
return
fp
;
}
/* Create a link, or copy a file. If the file is copied (not linked) then
* give a warning.
*/
static
void
linkorcopy
(
src
,
dst
,
sym
)
char
*
src
;
/* name of existing source file */
char
*
dst
;
/* name of new destination file */
int
sym
;
/* use symlink instead of link */
{
FILE
*
fpsrc
;
FILE
*
fpdst
;
int
c
;
/* Open the source file. We do this first to make sure it exists */
fpsrc
=
g_fopen
(
src
,
"rb"
);
if
(
!
fpsrc
)
{
purple_debug_error
(
"untar"
,
"Error opening: %s"
,
src
);
return
;
}
/* Create the destination file. On POSIX systems, this is just to
* make sure the directory path exists.
*/
fpdst
=
createpath
(
dst
);
if
(
!
fpdst
)
{
/* error message already given */
fclose
(
fpsrc
);
return
;
}
#ifdef _POSIX_SOURCE
# ifndef _WEAK_POSIX
/* first try to link it over, instead of copying */
fclose
(
fpdst
);
g_unlink
(
dst
);
if
(
sym
)
{
if
(
symlink
(
src
,
dst
))
{
perror
(
dst
);
}
fclose
(
fpsrc
);
return
;
}
if
(
!
link
(
src
,
dst
))
{
/* This story had a happy ending */
fclose
(
fpsrc
);
return
;
}
/* Dang. Reopen the destination again */
fpdst
=
g_fopen
(
dst
,
"wb"
);
/* This *can't* fail */
# endif
/* _WEAK_POSIX */
#endif
/* _POSIX_SOURCE */
/* Copy characters */
while
((
c
=
getc
(
fpsrc
))
!=
EOF
)
putc
(
c
,
fpdst
);
/* Close the files */
fclose
(
fpsrc
);
fclose
(
fpdst
);
/* Give a warning */
purple_debug_warning
(
"untar"
,
"%s: copy instead of link"
,
dst
);
}
/* This calls fwrite(), possibly after converting CR-LF to LF */
static
void
cvtwrite
(
blk
,
size
,
fp
)
Uchar_t
*
blk
;
/* the block to be written */
Ulong_t
size
;
/* number of characters to be written */
FILE
*
fp
;
/* file to write to */
{
Ulong_t
i
,
j
;
static
Uchar_t
mod
[
TSIZE
];
if
(
CONVERT
)
{
for
(
i
=
j
=
0
;
i
<
size
;
i
++
)
{
/* convert LF to local newline convention */
if
(
blk
[
i
]
==
LF
)
mod
[
j
++
]
=
'\n'
;
/* If CR-LF pair, then delete the CR */
else
if
(
blk
[
i
]
==
CR
&&
(
i
+
1
>=
size
||
blk
[
i
+
1
]
==
LF
))
;
/* other characters copied literally */
else
mod
[
j
++
]
=
blk
[
i
];
}
size
=
j
;
blk
=
mod
;
}
fwrite
(
blk
,
(
size_t
)
size
,
sizeof
(
Uchar_t
),
fp
);
}
/* Compute the checksum of a tar header block, and return it as a long int.
* The checksum can be computed using either POSIX rules (unsigned bytes)
* or Sun rules (signed bytes).
*/
static
long
checksum
(
tblk
,
sunny
)
tar_t
*
tblk
;
/* buffer containing the tar header block */
int
sunny
;
/* Boolean: Sun-style checksums? (else POSIX) */
{
long
sum
;
char
*
scan
;
/* compute the sum of the first 148 bytes -- everything up to but not
* including the checksum field itself.
*/
sum
=
0L
;
for
(
scan
=
(
char
*
)
tblk
;
scan
<
tblk
->
checksum
;
scan
++
)
{
sum
+=
(
*
scan
)
&
0xff
;
if
(
sunny
&&
(
*
scan
&
0x80
)
!=
0
)
sum
-=
256
;
}
/* for the 8 bytes of the checksum field, add blanks to the sum */
sum
+=
' '
*
sizeof
tblk
->
checksum
;
scan
+=
sizeof
tblk
->
checksum
;
/* finish counting the sum of the rest of the block */
for
(;
scan
<
(
char
*
)
tblk
+
sizeof
*
tblk
;
scan
++
)
{
sum
+=
(
*
scan
)
&
0xff
;
if
(
sunny
&&
(
*
scan
&
0x80
)
!=
0
)
sum
-=
256
;
}
return
sum
;
}
/* list files in an archive, and optionally extract them as well */
static
int
untar_block
(
Uchar_t
*
blk
)
{
static
char
nbuf
[
4096
];
/* storage space for prefix+name, combined */
static
char
*
name
,
*
n2
;
/* prefix and name, combined */
static
int
first
=
1
;
/* Boolean: first block of archive? */
long
sum
;
/* checksum for this block */
guint
i
;
tar_t
tblk
[
1
];
#ifdef _POSIX_SOURCE
static
mode_t
mode
;
/* file permissions */
static
struct
utimbuf
timestamp
;
/* file timestamp */
#endif
/* make a local copy of the block, and treat it as a tar header */
tblk
[
0
]
=
*
(
tar_t
*
)
blk
;
/* process each type of tape block differently */
if
(
outsize
>
TSIZE
)
{
/* data block, but not the last one */
if
(
outfp
)
cvtwrite
(
blk
,
(
Ulong_t
)
TSIZE
,
outfp
);
outsize
-=
TSIZE
;
}
else
if
(
outsize
>
0
)
{
/* last data block of current file */
if
(
outfp
)
{
cvtwrite
(
blk
,
outsize
,
outfp
);
fclose
(
outfp
);
outfp
=
NULL
;
#ifdef _POSIX_SOURCE
utime
(
nbuf
,
&
timestamp
);
chmod
(
nbuf
,
mode
);
#endif
}
outsize
=
0
;
}
else
if
((
tblk
)
->
filename
[
0
]
==
'\0'
)
{
/* end-of-archive marker */
if
(
didabs
)
purple_debug_warning
(
"untar"
,
"Removed leading slashes because
\"
ABSPATH option
\"
wasn't given."
);
return
1
;
}
else
{
/* file header */
/* half-assed verification -- does it look like header? */
if
((
tblk
)
->
filename
[
99
]
!=
'\0'
||
((
tblk
)
->
size
[
0
]
<
'0'
&&
(
tblk
)
->
size
[
0
]
!=
' '
)
||
(
tblk
)
->
size
[
0
]
>
'9'
)
{
if
(
first
)
{
purple_debug_error
(
"untar"
,
"%s: not a valid tar file"
,
inname
);
return
0
;
}
else
{
purple_debug_error
(
"untar"
,
"Garbage detected; preceding file may be damaged"
);
return
0
;
}
}
/* combine prefix and filename */
memset
(
nbuf
,
0
,
sizeof
nbuf
);
if
((
tblk
)
->
prefix
[
0
])
{
g_snprintf
(
nbuf
,
sizeof
(
nbuf
),
"%s/%s"
,
(
tblk
)
->
prefix
,
(
tblk
)
->
filename
);
}
else
{
g_strlcpy
(
nbuf
,
(
tblk
)
->
filename
,
sizeof
(
nbuf
));
}
/* Possibly strip the drive from the path */
if
(
!
ABSPATH
)
{
/* If the path contains a colon, assume everything before the
* colon is intended to be a drive name and ignore it. This
* should be just a single drive letter, but it should be safe
* to drop it even if it's longer. */
const
char
*
lastcolon
=
strrchr
(
nbuf
,
':'
);
if
(
lastcolon
)
{
memmove
(
nbuf
,
lastcolon
,
strlen
(
lastcolon
)
+
1
);
didabs
=
1
;
/* Path was changed from absolute to relative */
}
}
/* Convert any backslashes to forward slashes, and guard
* against doubled-up slashes. (Some DOS versions of "tar"
* get this wrong.) Also strip off leading slashes.
*/
name
=
nbuf
;
if
(
!
ABSPATH
&&
(
*
name
==
'/'
||
*
name
==
'\\'
))
didabs
=
1
;
for
(
n2
=
nbuf
;
*
name
;
name
++
)
{
if
(
*
name
==
'\\'
)
*
name
=
'/'
;
if
(
*
name
!=
'/'
||
(
ABSPATH
&&
n2
==
nbuf
)
||
(
n2
!=
nbuf
&&
n2
[
-1
]
!=
'/'
))
*
n2
++
=
*
name
;
}
if
(
n2
==
nbuf
)
*
n2
++
=
'/'
;
*
n2
=
'\0'
;
/* verify the checksum */
for
(
sum
=
0L
,
i
=
0
;
i
<
sizeof
((
tblk
)
->
checksum
);
i
++
)
{
if
((
tblk
)
->
checksum
[
i
]
>=
'0'
&&
(
tblk
)
->
checksum
[
i
]
<=
'7'
)
sum
=
sum
*
8
+
(
tblk
)
->
checksum
[
i
]
-
'0'
;
}
if
(
sum
!=
checksum
(
tblk
,
0
)
&&
sum
!=
checksum
(
tblk
,
1
))
{
if
(
!
first
)
{
purple_debug_error
(
"untar"
,
"Garbage detected; preceding file may be damaged"
);
}
purple_debug_error
(
"untar"
,
"%s: header has bad checksum for %s"
,
inname
,
nbuf
);
return
0
;
}
/* From this point on, we don't care whether this is the first
* block or not. Might as well reset the "first" flag now.
*/
first
=
0
;
/* if last character of name is '/' then assume directory */
if
(
*
nbuf
&&
nbuf
[
strlen
(
nbuf
)
-
1
]
==
'/'
)
(
tblk
)
->
type
=
'5'
;
/* convert file size */
for
(
outsize
=
0L
,
i
=
0
;
i
<
sizeof
((
tblk
)
->
size
);
i
++
)
{
if
((
tblk
)
->
size
[
i
]
>=
'0'
&&
(
tblk
)
->
size
[
i
]
<=
'7'
)
outsize
=
outsize
*
8
+
(
tblk
)
->
size
[
i
]
-
'0'
;
}
#ifdef _POSIX_SOURCE
/* convert file timestamp */
for
(
timestamp
.
modtime
=
0L
,
i
=
0
;
i
<
sizeof
((
tblk
)
->
mtime
);
i
++
)
{
if
((
tblk
)
->
mtime
[
i
]
>=
'0'
&&
(
tblk
)
->
mtime
[
i
]
<=
'7'
)
timestamp
.
modtime
=
timestamp
.
modtime
*
8
+
(
tblk
)
->
mtime
[
i
]
-
'0'
;
}
timestamp
.
actime
=
timestamp
.
modtime
;
/* convert file permissions */
for
(
mode
=
i
=
0
;
i
<
sizeof
((
tblk
)
->
mode
);
i
++
)
{
if
((
tblk
)
->
mode
[
i
]
>=
'0'
&&
(
tblk
)
->
mode
[
i
]
<=
'7'
)
mode
=
mode
*
8
+
(
tblk
)
->
mode
[
i
]
-
'0'
;
}
#endif
/* list the file */
if
(
VERBOSE
)
purple_debug_info
(
"untar"
,
"%c %s"
,
ISREGULAR
(
*
tblk
)
?
'-'
:
(
"hlcbdp"
[(
tblk
)
->
type
-
'1'
]),
nbuf
);
else
if
(
!
QUIET
)
purple_debug_info
(
"untar"
,
"%s"
,
nbuf
);
/* if link, then do the link-or-copy thing */
if
(
tblk
->
type
==
'1'
||
tblk
->
type
==
'2'
)
{
if
(
VERBOSE
)
purple_debug_info
(
"untar"
,
" -> %s"
,
tblk
->
linkto
);
if
(
!
LISTING
)
linkorcopy
(
tblk
->
linkto
,
nbuf
,
tblk
->
type
==
'2'
);
outsize
=
0L
;
return
1
;
}
/* If directory, then make a weak attempt to create it.
* Ideally we would do the "create path" thing, but that
* seems like more trouble than it's worth since traditional
* tar archives don't contain directories anyway.
*/
if
(
tblk
->
type
==
'5'
)
{
char
*
tmp
;
if
(
LISTING
)
tmp
=
" directory"
;
#ifdef _POSIX_SOURCE
else
if
(
mkdir
(
nbuf
,
mode
)
==
0
)
#else
else
if
(
g_mkdir
(
nbuf
,
0755
)
==
0
)
#endif
tmp
=
" created"
;
else
tmp
=
" ignored"
;
if
(
VERBOSE
)
purple_debug_info
(
"untar"
,
"%s"
,
tmp
);
return
1
;
}
/* if not a regular file, then skip it */
if
(
!
ISREGULAR
(
*
tblk
))
{
if
(
VERBOSE
)
purple_debug_info
(
"untar"
,
" ignored"
);
outsize
=
0L
;
return
1
;
}
/* print file statistics */
if
(
VERBOSE
)
{
purple_debug_info
(
"untar"
,
" (%ld byte%s, %ld tape block%s)"
,
outsize
,
outsize
==
1
?
""
:
"s"
,
(
outsize
+
TSIZE
-
1
)
/
TSIZE
,
(
outsize
>
0
&&
outsize
<=
TSIZE
)
?
""
:
"s"
);
}
/* if extracting, then try to create the file */
if
(
!
LISTING
)
outfp
=
createpath
(
nbuf
);
else
outfp
=
NULL
;
/* if file is 0 bytes long, then we're done already! */
if
(
outsize
==
0
&&
outfp
)
{
fclose
(
outfp
);
#ifdef _POSIX_SOURCE
utime
(
nbuf
,
&
timestamp
);
chmod
(
nbuf
,
mode
);
#endif
}
}
return
1
;
}
/* Process an archive file. This involves reading the blocks one at a time
* and passing them to a untar() function.
*/
int
untar
(
const
char
*
filename
,
const
char
*
destdir
,
untar_opt
options
)
{
int
ret
=
1
;
wchar_t
curdir
[
_MAX_PATH
];
wchar_t
*
w_destdir
;
untarops
=
options
;
/* open the archive */
inname
=
filename
;
infp
=
g_fopen
(
filename
,
"rb"
);
if
(
!
infp
)
{
purple_debug_error
(
"untar"
,
"Error opening: %s"
,
filename
);
return
0
;
}
w_destdir
=
g_utf8_to_utf16
(
destdir
,
-1
,
NULL
,
NULL
,
NULL
);
/* Set current directory */
if
(
!
GetCurrentDirectoryW
(
_MAX_PATH
,
curdir
))
{
purple_debug_error
(
"untar"
,
"Could not get current directory (error %lu)."
,
GetLastError
());
fclose
(
infp
);
return
0
;
}
if
(
!
SetCurrentDirectoryW
(
w_destdir
))
{
purple_debug_error
(
"untar"
,
"Could not set current directory to (error %lu): %s"
,
GetLastError
(),
destdir
);
fclose
(
infp
);
return
0
;
}
else
{
/* UNCOMPRESSED */
/* send each block to the untar_block() function */
while
(
fread
(
slide
,
1
,
TSIZE
,
infp
)
==
TSIZE
)
{
if
(
!
untar_block
(
slide
))
{
purple_debug_error
(
"untar"
,
"untar failure: %s"
,
filename
);
fclose
(
infp
);
ret
=
0
;
}
}
if
(
outsize
>
0
&&
ret
)
{
purple_debug_warning
(
"untar"
,
"Last file might be truncated!"
);
fclose
(
outfp
);
outfp
=
NULL
;
}
if
(
!
SetCurrentDirectoryW
(
curdir
))
{
purple_debug_error
(
"untar"
,
"Could not set current dir back to original (error %lu)."
,
GetLastError
());
ret
=
0
;
}
}
g_free
(
w_destdir
);
/* close the archive file. */
fclose
(
infp
);
return
ret
;
}