0,0 → 1,653 |
/* |
Copyright (c) 1990-2007 Info-ZIP. All rights reserved. |
|
See the accompanying file LICENSE, version 2005-Feb-10 or later |
(the contents of which are also included in (un)zip.h) for terms of use. |
If, for some reason, all these files are missing, the Info-ZIP license |
also may be found at: ftp://ftp.info-zip.org/pub/infozip/license.html |
*/ |
/* |
crypt.c (full version) by Info-ZIP. Last revised: [see crypt.h] |
|
The main encryption/decryption source code for Info-Zip software was |
originally written in Europe. To the best of our knowledge, it can |
be freely distributed in both source and object forms from any country, |
including the USA under License Exception TSU of the U.S. Export |
Administration Regulations (section 740.13(e)) of 6 June 2002. |
|
NOTE on copyright history: |
Previous versions of this source package (up to version 2.8) were |
not copyrighted and put in the public domain. If you cannot comply |
with the Info-Zip LICENSE, you may want to look for one of those |
public domain versions. |
*/ |
|
/* |
This encryption code is a direct transcription of the algorithm from |
Roger Schlafly, described by Phil Katz in the file appnote.txt. This |
file (appnote.txt) is distributed with the PKZIP program (even in the |
version without encryption capabilities). |
*/ |
|
#define ZCRYPT_INTERNAL |
#include "zip.h" |
#include "crypt.h" |
#include "ttyio.h" |
|
#if CRYPT |
|
#ifndef FALSE |
# define FALSE 0 |
#endif |
|
#ifdef ZIP |
/* For the encoding task used in Zip (and ZipCloak), we want to initialize |
the crypt algorithm with some reasonably unpredictable bytes, see |
the crypthead() function. The standard rand() library function is |
used to supply these `random' bytes, which in turn is initialized by |
a srand() call. The srand() function takes an "unsigned" (at least 16bit) |
seed value as argument to determine the starting point of the rand() |
pseudo-random number generator. |
This seed number is constructed as "Seed = Seed1 .XOR. Seed2" with |
Seed1 supplied by the current time (= "(unsigned)time()") and Seed2 |
as some (hopefully) nondeterministic bitmask. On many (most) systems, |
we use some "process specific" number, as the PID or something similar, |
but when nothing unpredictable is available, a fixed number may be |
sufficient. |
NOTE: |
1.) This implementation requires the availability of the following |
standard UNIX C runtime library functions: time(), rand(), srand(). |
On systems where some of them are missing, the environment that |
incorporates the crypt routines must supply suitable replacement |
functions. |
2.) It is a very bad idea to use a second call to time() to set the |
"Seed2" number! In this case, both "Seed1" and "Seed2" would be |
(almost) identical, resulting in a (mostly) "zero" constant seed |
number passed to srand(). |
|
The implementation environment defined in the "zip.h" header should |
supply a reasonable definition for ZCR_SEED2 (an unsigned number; for |
most implementations of rand() and srand(), only the lower 16 bits are |
significant!). An example that works on many systems would be |
"#define ZCR_SEED2 (unsigned)getpid()". |
The default definition for ZCR_SEED2 supplied below should be regarded |
as a fallback to allow successful compilation in "beta state" |
environments. |
*/ |
# include <time.h> /* time() function supplies first part of crypt seed */ |
/* "last resort" source for second part of crypt seed pattern */ |
# ifndef ZCR_SEED2 |
# define ZCR_SEED2 (unsigned)3141592654L /* use PI as default pattern */ |
# endif |
# ifdef GLOBAL /* used in Amiga system headers, maybe others too */ |
# undef GLOBAL |
# endif |
# define GLOBAL(g) g |
#else /* !ZIP */ |
# define GLOBAL(g) G.g |
#endif /* ?ZIP */ |
|
|
#ifdef UNZIP |
/* char *key = (char *)NULL; moved to globals.h */ |
# ifndef FUNZIP |
local int testp OF((__GPRO__ ZCONST uch *h)); |
local int testkey OF((__GPRO__ ZCONST uch *h, ZCONST char *key)); |
# endif |
#endif /* UNZIP */ |
|
#ifndef UNZIP /* moved to globals.h for UnZip */ |
# ifndef Z_UINT4_DEFINED |
# if !defined(NO_LIMITS_H) |
# if (defined(UINT_MAX) && (UINT_MAX == 0xffffffffUL)) |
typedef unsigned int z_uint4; |
# define Z_UINT4_DEFINED |
# else |
# if (defined(ULONG_MAX) && (ULONG_MAX == 0xffffffffUL)) |
typedef unsigned long z_uint4; |
# define Z_UINT4_DEFINED |
# else |
# if (defined(USHRT_MAX) && (USHRT_MAX == 0xffffffffUL)) |
typedef unsigned short z_uint4; |
# define Z_UINT4_DEFINED |
# endif |
# endif |
# endif |
# endif /* !NO_LIMITS_H */ |
# endif /* !Z_UINT4_DEFINED */ |
# ifndef Z_UINT4_DEFINED |
typedef ulg z_uint4; |
# define Z_UINT4_DEFINED |
# endif |
local z_uint4 keys[3]; /* keys defining the pseudo-random sequence */ |
#endif /* !UNZIP */ |
|
#ifndef Trace |
# ifdef CRYPT_DEBUG |
# define Trace(x) fprintf x |
# else |
# define Trace(x) |
# endif |
#endif |
|
#include "crc32.h" |
|
#ifdef IZ_CRC_BE_OPTIMIZ |
local z_uint4 near crycrctab[256]; |
local z_uint4 near *cry_crctb_p = NULL; |
local z_uint4 near *crytab_init OF((__GPRO)); |
# define CRY_CRC_TAB cry_crctb_p |
# undef CRC32 |
# define CRC32(c, b, crctab) (crctab[((int)(c) ^ (b)) & 0xff] ^ ((c) >> 8)) |
#else |
# define CRY_CRC_TAB CRC_32_TAB |
#endif /* ?IZ_CRC_BE_OPTIMIZ */ |
|
/*********************************************************************** |
* Return the next byte in the pseudo-random sequence |
*/ |
int decrypt_byte(__G) |
__GDEF |
{ |
unsigned temp; /* POTENTIAL BUG: temp*(temp^1) may overflow in an |
* unpredictable manner on 16-bit systems; not a problem |
* with any known compiler so far, though */ |
|
temp = ((unsigned)GLOBAL(keys[2]) & 0xffff) | 2; |
return (int)(((temp * (temp ^ 1)) >> 8) & 0xff); |
} |
|
/*********************************************************************** |
* Update the encryption keys with the next byte of plain text |
*/ |
int update_keys(__G__ c) |
__GDEF |
int c; /* byte of plain text */ |
{ |
GLOBAL(keys[0]) = CRC32(GLOBAL(keys[0]), c, CRY_CRC_TAB); |
GLOBAL(keys[1]) = (GLOBAL(keys[1]) |
+ (GLOBAL(keys[0]) & 0xff)) |
* 134775813L + 1; |
{ |
register int keyshift = (int)(GLOBAL(keys[1]) >> 24); |
GLOBAL(keys[2]) = CRC32(GLOBAL(keys[2]), keyshift, CRY_CRC_TAB); |
} |
return c; |
} |
|
|
/*********************************************************************** |
* Initialize the encryption keys and the random header according to |
* the given password. |
*/ |
void init_keys(__G__ passwd) |
__GDEF |
ZCONST char *passwd; /* password string with which to modify keys */ |
{ |
#ifdef IZ_CRC_BE_OPTIMIZ |
if (cry_crctb_p == NULL) { |
cry_crctb_p = crytab_init(__G); |
} |
#endif |
GLOBAL(keys[0]) = 305419896L; |
GLOBAL(keys[1]) = 591751049L; |
GLOBAL(keys[2]) = 878082192L; |
while (*passwd != '\0') { |
update_keys(__G__ (int)*passwd); |
passwd++; |
} |
} |
|
|
/*********************************************************************** |
* Initialize the local copy of the table of precomputed crc32 values. |
* Whereas the public crc32-table is optimized for crc32 calculations |
* on arrays of bytes, the crypt code needs the crc32 values in an |
* byte-order-independent form as 32-bit unsigned numbers. On systems |
* with Big-Endian byte order using the optimized crc32 code, this |
* requires inverting the byte-order of the values in the |
* crypt-crc32-table. |
*/ |
#ifdef IZ_CRC_BE_OPTIMIZ |
local z_uint4 near *crytab_init(__G) |
__GDEF |
{ |
int i; |
|
for (i = 0; i < 256; i++) { |
crycrctab[i] = REV_BE(CRC_32_TAB[i]); |
} |
return crycrctab; |
} |
#endif |
|
|
#ifdef ZIP |
|
/*********************************************************************** |
* Write encryption header to file zfile using the password passwd |
* and the cyclic redundancy check crc. |
*/ |
void crypthead(passwd, crc, zfile) |
ZCONST char *passwd; /* password string */ |
ulg crc; /* crc of file being encrypted */ |
FILE *zfile; /* where to write header */ |
{ |
int n; /* index in random header */ |
int t; /* temporary */ |
int c; /* random byte */ |
uch header[RAND_HEAD_LEN]; /* random header */ |
static unsigned calls = 0; /* ensure different random header each time */ |
|
/* First generate RAND_HEAD_LEN-2 random bytes. We encrypt the |
* output of rand() to get less predictability, since rand() is |
* often poorly implemented. |
*/ |
if (++calls == 1) { |
srand((unsigned)time(NULL) ^ ZCR_SEED2); |
} |
init_keys(passwd); |
for (n = 0; n < RAND_HEAD_LEN-2; n++) { |
c = (rand() >> 7) & 0xff; |
header[n] = (uch)zencode(c, t); |
} |
/* Encrypt random header (last two bytes is high word of crc) */ |
init_keys(passwd); |
for (n = 0; n < RAND_HEAD_LEN-2; n++) { |
header[n] = (uch)zencode(header[n], t); |
} |
header[RAND_HEAD_LEN-2] = (uch)zencode((int)(crc >> 16) & 0xff, t); |
header[RAND_HEAD_LEN-1] = (uch)zencode((int)(crc >> 24) & 0xff, t); |
fwrite(header, 1, RAND_HEAD_LEN, f); |
} |
|
|
#ifdef UTIL |
|
/*********************************************************************** |
* Encrypt the zip entry described by z from file source to file dest |
* using the password passwd. Return an error code in the ZE_ class. |
*/ |
int zipcloak(z, source, dest, passwd) |
struct zlist far *z; /* zip entry to encrypt */ |
FILE *source, *dest; /* source and destination files */ |
ZCONST char *passwd; /* password string */ |
{ |
int c; /* input byte */ |
int res; /* result code */ |
ulg n; /* holds offset and counts size */ |
ush flag; /* previous flags */ |
int t; /* temporary */ |
int ztemp; /* temporary storage for zencode value */ |
|
/* Set encrypted bit, clear extended local header bit and write local |
header to output file */ |
if ((n = (ulg)ftell(dest)) == (ulg)-1L) return ZE_TEMP; |
z->off = n; |
flag = z->flg; |
z->flg |= 1, z->flg &= ~8; |
z->lflg |= 1, z->lflg &= ~8; |
z->siz += RAND_HEAD_LEN; |
if ((res = putlocal(z, dest)) != ZE_OK) return res; |
|
/* Initialize keys with password and write random header */ |
crypthead(passwd, z->crc, dest); |
|
/* Skip local header in input file */ |
if (fseek(source, (long)((4 + LOCHEAD) + (ulg)z->nam + (ulg)z->ext), |
SEEK_CUR)) { |
return ferror(source) ? ZE_READ : ZE_EOF; |
} |
|
/* Encrypt data */ |
for (n = z->siz - RAND_HEAD_LEN; n; n--) { |
if ((c = getc(source)) == EOF) { |
return ferror(source) ? ZE_READ : ZE_EOF; |
} |
ztemp = zencode(c, t); |
putc(ztemp, dest); |
} |
/* Skip extended local header in input file if there is one */ |
if ((flag & 8) != 0 && fseek(source, 16L, SEEK_CUR)) { |
return ferror(source) ? ZE_READ : ZE_EOF; |
} |
if (fflush(dest) == EOF) return ZE_TEMP; |
|
/* Update number of bytes written to output file */ |
tempzn += (4 + LOCHEAD) + z->nam + z->ext + z->siz; |
|
return ZE_OK; |
} |
|
/*********************************************************************** |
* Decrypt the zip entry described by z from file source to file dest |
* using the password passwd. Return an error code in the ZE_ class. |
*/ |
int zipbare(z, source, dest, passwd) |
struct zlist far *z; /* zip entry to encrypt */ |
FILE *source, *dest; /* source and destination files */ |
ZCONST char *passwd; /* password string */ |
{ |
#ifdef ZIP10 |
int c0 /* byte preceding the last input byte */ |
#endif |
int c1; /* last input byte */ |
ulg offset; /* used for file offsets */ |
ulg size; /* size of input data */ |
int r; /* size of encryption header */ |
int res; /* return code */ |
ush flag; /* previous flags */ |
|
/* Save position and skip local header in input file */ |
if ((offset = (ulg)ftell(source)) == (ulg)-1L || |
fseek(source, (long)((4 + LOCHEAD) + (ulg)z->nam + (ulg)z->ext), |
SEEK_CUR)) { |
return ferror(source) ? ZE_READ : ZE_EOF; |
} |
/* Initialize keys with password */ |
init_keys(passwd); |
|
/* Decrypt encryption header, save last two bytes */ |
c1 = 0; |
for (r = RAND_HEAD_LEN; r; r--) { |
#ifdef ZIP10 |
c0 = c1; |
#endif |
if ((c1 = getc(source)) == EOF) { |
return ferror(source) ? ZE_READ : ZE_EOF; |
} |
Trace((stdout, " (%02x)", c1)); |
zdecode(c1); |
Trace((stdout, " %02x", c1)); |
} |
Trace((stdout, "\n")); |
|
/* If last two bytes of header don't match crc (or file time in the |
* case of an extended local header), back up and just copy. For |
* pkzip 2.0, the check has been reduced to one byte only. |
*/ |
#ifdef ZIP10 |
if ((ush)(c0 | (c1<<8)) != |
(z->flg & 8 ? (ush) z->tim & 0xffff : (ush)(z->crc >> 16))) { |
#else |
if ((ush)c1 != (z->flg & 8 ? (ush) z->tim >> 8 : (ush)(z->crc >> 24))) { |
#endif |
if (fseek(source, offset, SEEK_SET)) { |
return ferror(source) ? ZE_READ : ZE_EOF; |
} |
if ((res = zipcopy(z, source, dest)) != ZE_OK) return res; |
return ZE_MISS; |
} |
|
/* Clear encrypted bit and local header bit, and write local header to |
output file */ |
if ((offset = (ulg)ftell(dest)) == (ulg)-1L) return ZE_TEMP; |
z->off = offset; |
flag = z->flg; |
z->flg &= ~9; |
z->lflg &= ~9; |
z->siz -= RAND_HEAD_LEN; |
if ((res = putlocal(z, dest)) != ZE_OK) return res; |
|
/* Decrypt data */ |
for (size = z->siz; size; size--) { |
if ((c1 = getc(source)) == EOF) { |
return ferror(source) ? ZE_READ : ZE_EOF; |
} |
zdecode(c1); |
putc(c1, dest); |
} |
/* Skip extended local header in input file if there is one */ |
if ((flag & 8) != 0 && fseek(source, 16L, SEEK_CUR)) { |
return ferror(source) ? ZE_READ : ZE_EOF; |
} |
if (fflush(dest) == EOF) return ZE_TEMP; |
|
/* Update number of bytes written to output file */ |
tempzn += (4 + LOCHEAD) + z->nam + z->ext + z->siz; |
|
return ZE_OK; |
} |
|
|
#else /* !UTIL */ |
|
/*********************************************************************** |
* If requested, encrypt the data in buf, and in any case call fwrite() |
* with the arguments to zfwrite(). Return what fwrite() returns. |
* |
* A bug has been found when encrypting large files. See trees.c |
* for details and the fix. |
*/ |
unsigned zfwrite(buf, item_size, nb, f) |
zvoid *buf; /* data buffer */ |
extent item_size; /* size of each item in bytes */ |
extent nb; /* number of items */ |
FILE *f; /* file to write to */ |
{ |
int t; /* temporary */ |
|
if (key != (char *)NULL) { /* key is the global password pointer */ |
ulg size; /* buffer size */ |
char *p = (char*)buf; /* steps through buffer */ |
|
/* Encrypt data in buffer */ |
for (size = item_size*(ulg)nb; size != 0; p++, size--) { |
*p = (char)zencode(*p, t); |
} |
} |
/* Write the buffer out */ |
return fwrite(buf, item_size, nb, f); |
} |
|
#endif /* ?UTIL */ |
#endif /* ZIP */ |
|
|
#if (defined(UNZIP) && !defined(FUNZIP)) |
|
/*********************************************************************** |
* Get the password and set up keys for current zipfile member. |
* Return PK_ class error. |
*/ |
int decrypt(__G__ passwrd) |
__GDEF |
ZCONST char *passwrd; |
{ |
ush b; |
int n, r; |
uch h[RAND_HEAD_LEN]; |
|
Trace((stdout, "\n[incnt = %d]: ", GLOBAL(incnt))); |
|
/* get header once (turn off "encrypted" flag temporarily so we don't |
* try to decrypt the same data twice) */ |
GLOBAL(pInfo->encrypted) = FALSE; |
defer_leftover_input(__G); |
for (n = 0; n < RAND_HEAD_LEN; n++) { |
b = NEXTBYTE; |
h[n] = (uch)b; |
Trace((stdout, " (%02x)", h[n])); |
} |
undefer_input(__G); |
GLOBAL(pInfo->encrypted) = TRUE; |
|
if (GLOBAL(newzip)) { /* this is first encrypted member in this zipfile */ |
GLOBAL(newzip) = FALSE; |
if (passwrd != (char *)NULL) { /* user gave password on command line */ |
if (!GLOBAL(key)) { |
if ((GLOBAL(key) = (char *)malloc(strlen(passwrd)+1)) == |
(char *)NULL) |
return PK_MEM2; |
strcpy(GLOBAL(key), passwrd); |
GLOBAL(nopwd) = TRUE; /* inhibit password prompting! */ |
} |
} else if (GLOBAL(key)) { /* get rid of previous zipfile's key */ |
free(GLOBAL(key)); |
GLOBAL(key) = (char *)NULL; |
} |
} |
|
/* if have key already, test it; else allocate memory for it */ |
if (GLOBAL(key)) { |
if (!testp(__G__ h)) |
return PK_COOL; /* existing password OK (else prompt for new) */ |
else if (GLOBAL(nopwd)) |
return PK_WARN; /* user indicated no more prompting */ |
} else if ((GLOBAL(key) = (char *)malloc(IZ_PWLEN+1)) == (char *)NULL) |
return PK_MEM2; |
|
/* try a few keys */ |
n = 0; |
do { |
r = (*G.decr_passwd)((zvoid *)&G, &n, GLOBAL(key), IZ_PWLEN+1, |
GLOBAL(zipfn), GLOBAL(filename)); |
if (r == IZ_PW_ERROR) { /* internal error in fetch of PW */ |
free (GLOBAL(key)); |
GLOBAL(key) = NULL; |
return PK_MEM2; |
} |
if (r != IZ_PW_ENTERED) { /* user replied "skip" or "skip all" */ |
*GLOBAL(key) = '\0'; /* We try the NIL password, ... */ |
n = 0; /* and cancel fetch for this item. */ |
} |
if (!testp(__G__ h)) |
return PK_COOL; |
if (r == IZ_PW_CANCELALL) /* User replied "Skip all" */ |
GLOBAL(nopwd) = TRUE; /* inhibit any further PW prompt! */ |
} while (n > 0); |
|
return PK_WARN; |
|
} /* end function decrypt() */ |
|
|
|
/*********************************************************************** |
* Test the password. Return -1 if bad, 0 if OK. |
*/ |
local int testp(__G__ h) |
__GDEF |
ZCONST uch *h; |
{ |
int r; |
char *key_translated; |
|
/* On systems with "obscure" native character coding (e.g., EBCDIC), |
* the first test translates the password to the "main standard" |
* character coding. */ |
|
#ifdef STR_TO_CP1 |
/* allocate buffer for translated password */ |
if ((key_translated = malloc(strlen(GLOBAL(key)) + 1)) == (char *)NULL) |
return -1; |
/* first try, test password translated "standard" charset */ |
r = testkey(__G__ h, STR_TO_CP1(key_translated, GLOBAL(key))); |
#else /* !STR_TO_CP1 */ |
/* first try, test password as supplied on the extractor's host */ |
r = testkey(__G__ h, GLOBAL(key)); |
#endif /* ?STR_TO_CP1 */ |
|
#ifdef STR_TO_CP2 |
if (r != 0) { |
#ifndef STR_TO_CP1 |
/* now prepare for second (and maybe third) test with translated pwd */ |
if ((key_translated = malloc(strlen(GLOBAL(key)) + 1)) == (char *)NULL) |
return -1; |
#endif |
/* second try, password translated to alternate ("standard") charset */ |
r = testkey(__G__ h, STR_TO_CP2(key_translated, GLOBAL(key))); |
#ifdef STR_TO_CP3 |
if (r != 0) |
/* third try, password translated to another "standard" charset */ |
r = testkey(__G__ h, STR_TO_CP3(key_translated, GLOBAL(key))); |
#endif |
#ifndef STR_TO_CP1 |
free(key_translated); |
#endif |
} |
#endif /* STR_TO_CP2 */ |
|
#ifdef STR_TO_CP1 |
free(key_translated); |
if (r != 0) { |
/* last resort, test password as supplied on the extractor's host */ |
r = testkey(__G__ h, GLOBAL(key)); |
} |
#endif /* STR_TO_CP1 */ |
|
return r; |
|
} /* end function testp() */ |
|
|
local int testkey(__G__ h, key) |
__GDEF |
ZCONST uch *h; /* decrypted header */ |
ZCONST char *key; /* decryption password to test */ |
{ |
ush b; |
#ifdef ZIP10 |
ush c; |
#endif |
int n; |
uch *p; |
uch hh[RAND_HEAD_LEN]; /* decrypted header */ |
|
/* set keys and save the encrypted header */ |
init_keys(__G__ key); |
memcpy(hh, h, RAND_HEAD_LEN); |
|
/* check password */ |
for (n = 0; n < RAND_HEAD_LEN; n++) { |
zdecode(hh[n]); |
Trace((stdout, " %02x", hh[n])); |
} |
|
Trace((stdout, |
"\n lrec.crc= %08lx crec.crc= %08lx pInfo->ExtLocHdr= %s\n", |
GLOBAL(lrec.crc32), GLOBAL(pInfo->crc), |
GLOBAL(pInfo->ExtLocHdr) ? "true":"false")); |
Trace((stdout, " incnt = %d unzip offset into zipfile = %ld\n", |
GLOBAL(incnt), |
GLOBAL(cur_zipfile_bufstart)+(GLOBAL(inptr)-GLOBAL(inbuf)))); |
|
/* same test as in zipbare(): */ |
|
#ifdef ZIP10 /* check two bytes */ |
c = hh[RAND_HEAD_LEN-2], b = hh[RAND_HEAD_LEN-1]; |
Trace((stdout, |
" (c | (b<<8)) = %04x (crc >> 16) = %04x lrec.time = %04x\n", |
(ush)(c | (b<<8)), (ush)(GLOBAL(lrec.crc32) >> 16), |
((ush)GLOBAL(lrec.last_mod_dos_datetime) & 0xffff)))); |
if ((ush)(c | (b<<8)) != (GLOBAL(pInfo->ExtLocHdr) ? |
((ush)GLOBAL(lrec.last_mod_dos_datetime) & 0xffff) : |
(ush)(GLOBAL(lrec.crc32) >> 16))) |
return -1; /* bad */ |
#else |
b = hh[RAND_HEAD_LEN-1]; |
Trace((stdout, " b = %02x (crc >> 24) = %02x (lrec.time >> 8) = %02x\n", |
b, (ush)(GLOBAL(lrec.crc32) >> 24), |
((ush)GLOBAL(lrec.last_mod_dos_datetime) >> 8) & 0xff)); |
if (b != (GLOBAL(pInfo->ExtLocHdr) ? |
((ush)GLOBAL(lrec.last_mod_dos_datetime) >> 8) & 0xff : |
(ush)(GLOBAL(lrec.crc32) >> 24))) |
return -1; /* bad */ |
#endif |
/* password OK: decrypt current buffer contents before leaving */ |
for (n = (long)GLOBAL(incnt) > GLOBAL(csize) ? |
(int)GLOBAL(csize) : GLOBAL(incnt), |
p = GLOBAL(inptr); n--; p++) |
zdecode(*p); |
return 0; /* OK */ |
|
} /* end function testkey() */ |
|
#endif /* UNZIP && !FUNZIP */ |
|
#else /* !CRYPT */ |
|
/* something "externally visible" to shut up compiler/linker warnings */ |
int zcr_dummy; |
|
#endif /* ?CRYPT */ |