pidgin/pidgin
Clone
Summary
Browse
Changes
Graph
Replace g_strdup_printf("%s", string) with g_strdup(string)
2021-01-13, Arkadiy Illarionov
4c3ee00e6107
Replace g_strdup_printf("%s", string) with g_strdup(string)
Reduce duplication after replacing where applicable.
Reviewed at https://reviews.imfreedom.org/r/420/
/* 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 untar_error( error, args... ) purple_debug(PURPLE_DEBUG_ERROR, "untar", error, ## args )
#define untar_warning( warning, args... ) purple_debug(PURPLE_DEBUG_WARNING, "untar", warning, ## args )
#define untar_verbose( args... ) purple_debug(PURPLE_DEBUG_INFO, "untar", ## args )
#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
)
{
untar_warning
(
"%s: exists, will not overwrite without
\"
FORCE option
\"\n
"
,
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
)
untar_error
(
"Error opening: %s
\n
"
,
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
)
{
untar_error
(
"Error opening: %s
\n
"
,
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 */
untar_warning
(
"%s: copy instead of link
\n
"
,
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
)
untar_warning
(
"Removed leading slashes because
\"
ABSPATH option
\"
wasn't given.
\n
"
);
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
)
{
untar_error
(
"%s: not a valid tar file
\n
"
,
inname
);
return
0
;
}
else
{
untar_error
(
"Garbage detected; preceding file may be damaged
\n
"
);
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
)
untar_error
(
"Garbage detected; preceding file may be damaged
\n
"
);
untar_error
(
"%s: header has bad checksum for %s
\n
"
,
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
)
untar_verbose
(
"%c %s"
,
ISREGULAR
(
*
tblk
)
?
'-'
:
(
"hlcbdp"
[(
tblk
)
->
type
-
'1'
]),
nbuf
);
else
if
(
!
QUIET
)
untar_verbose
(
"%s
\n
"
,
nbuf
);
/* if link, then do the link-or-copy thing */
if
(
tblk
->
type
==
'1'
||
tblk
->
type
==
'2'
)
{
if
(
VERBOSE
)
untar_verbose
(
" -> %s
\n
"
,
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
)
untar_verbose
(
"%s
\n
"
,
tmp
);
return
1
;
}
/* if not a regular file, then skip it */
if
(
!
ISREGULAR
(
*
tblk
))
{
if
(
VERBOSE
)
untar_verbose
(
" ignored
\n
"
);
outsize
=
0L
;
return
1
;
}
/* print file statistics */
if
(
VERBOSE
)
{
untar_verbose
(
" (%ld byte%s, %ld tape block%s)
\n
"
,
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
)
{
untar_error
(
"Error opening: %s
\n
"
,
filename
);
return
0
;
}
w_destdir
=
g_utf8_to_utf16
(
destdir
,
-1
,
NULL
,
NULL
,
NULL
);
/* Set current directory */
if
(
!
GetCurrentDirectoryW
(
_MAX_PATH
,
curdir
))
{
untar_error
(
"Could not get current directory (error %lu).
\n
"
,
GetLastError
());
fclose
(
infp
);
return
0
;
}
if
(
!
SetCurrentDirectoryW
(
w_destdir
))
{
untar_error
(
"Could not set current directory to (error %lu): %s
\n
"
,
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
))
{
untar_error
(
"untar failure: %s
\n
"
,
filename
);
fclose
(
infp
);
ret
=
0
;
}
}
if
(
outsize
>
0
&&
ret
)
{
untar_warning
(
"Last file might be truncated!
\n
"
);
fclose
(
outfp
);
outfp
=
NULL
;
}
if
(
!
SetCurrentDirectoryW
(
curdir
))
{
untar_error
(
"Could not set current dir back to original (error %lu).
\n
"
,
GetLastError
());
ret
=
0
;
}
}
g_free
(
w_destdir
);
/* close the archive file. */
fclose
(
infp
);
return
ret
;
}