f1deb8e330
Fixes "warning: 'register' storage class specifier is deprecated and incompatible with C++17 [-Wdeprecated-register]" with clang https://en.cppreference.com/w/cpp/keyword/register
527 lines
13 KiB
C
527 lines
13 KiB
C
/***************************************************************************
|
|
|
|
main.c
|
|
|
|
(c) 2003-2004 Daniel Campos Fernández <danielcampos@netcourrier.com>
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2, or (at your option)
|
|
any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
MA 02110-1301, USA.
|
|
|
|
***************************************************************************/
|
|
|
|
#define __MAIN_C
|
|
|
|
#include "gb_common.h"
|
|
|
|
// Use 64 bits I/O
|
|
#if (__WORDSIZE == 64) && (!_LARGEFILE64_SOURCE)
|
|
#define _LARGEFILE64_SOURCE
|
|
#endif
|
|
|
|
#include <errno.h>
|
|
#include <stdio.h>
|
|
#include <zlib.h>
|
|
|
|
#include "main.h"
|
|
|
|
#define GB_Z_BUFFER 8192
|
|
#define MODE_READ 0
|
|
#define MODE_WRITE 1
|
|
|
|
GB_INTERFACE EXPORT GB;
|
|
COMPRESS_INTERFACE EXPORT COMPRESSION;
|
|
|
|
static COMPRESS_DRIVER _driver;
|
|
|
|
GB_STREAM_DESC ZStream = {
|
|
.open = CZ_stream_open,
|
|
.close = CZ_stream_close,
|
|
.read = CZ_stream_read,
|
|
.write = CZ_stream_write,
|
|
.seek = CZ_stream_seek,
|
|
.tell = CZ_stream_tell,
|
|
.flush = CZ_stream_flush,
|
|
.eof = CZ_stream_eof,
|
|
.lof = CZ_stream_lof
|
|
};
|
|
|
|
typedef
|
|
struct {
|
|
GB_STREAM_BASE base;
|
|
int mode;
|
|
gzFile handle;
|
|
}
|
|
STREAM_COMPRESS;
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
The driver interface
|
|
|
|
*****************************************************************************/
|
|
|
|
/*********************************************************************************
|
|
The Driver must provide this function:
|
|
|
|
int max_compression(void)
|
|
|
|
It must return the maximum compression level that user can
|
|
assign, for instance gzip (in this moment) provides ten values, from zero to
|
|
9 , beeing 9 the best compression level (more compression),
|
|
so the driver here returns '9'
|
|
*********************************************************************************/
|
|
|
|
static int max_compression(void)
|
|
{
|
|
return Z_BEST_COMPRESSION;
|
|
}
|
|
|
|
/*********************************************************************************
|
|
The Driver must provide this function:
|
|
|
|
int min_compression(void)
|
|
|
|
It must return the minimum compression level that user can
|
|
assign, for instance gzip (in this moment) provides ten values, from zero to
|
|
9 , beeing 0 the worst compression level (no compression at all),
|
|
so the driver here returns '0'
|
|
*********************************************************************************/
|
|
static int min_compression(void)
|
|
{
|
|
return Z_NO_COMPRESSION;
|
|
}
|
|
/*********************************************************************************
|
|
The Driver must provide this function:
|
|
|
|
int default_compression(void)
|
|
|
|
It must return the default compression level, that is, the value to send when
|
|
Compression object methods are called without the optional parameter 'Level'.
|
|
In this gzip wrapper, for example, this value is -1
|
|
*********************************************************************************/
|
|
static int default_compression(void)
|
|
{
|
|
return Z_DEFAULT_COMPRESSION;
|
|
}
|
|
/*********************************************************************************
|
|
The Driver must provide this function:
|
|
|
|
void c_String(char **target,unsigned int *lent,char *source,unsigned int len,int level)
|
|
|
|
It is called to compress a String and return it compressed. The object will
|
|
pass the following values:
|
|
|
|
target = NULL
|
|
lent = 0
|
|
source = a pointer to the original string
|
|
len = length of the original string
|
|
level = compression level
|
|
|
|
You will never receive a zero lenght string, nor a erroneus 'level' value
|
|
|
|
The function must store the compressed string in 'target', and its length in 'lent',
|
|
or NULL and zero if it fails by any reason
|
|
|
|
*********************************************************************************/
|
|
static void c_String(char **target,unsigned int *lent,char *source,unsigned int len,int level)
|
|
{
|
|
unsigned long l;
|
|
*lent=0;
|
|
*target=NULL;
|
|
|
|
if (!len) return;
|
|
|
|
*lent=len + (len*0.1) + 15;
|
|
|
|
GB.Alloc ((void**)target,sizeof(char)*(*lent));
|
|
|
|
l = *lent;
|
|
if (Z_OK != compress2 ((Bytef*)(*target),&l,(Bytef *)source,len,level))
|
|
{
|
|
GB.Free((void**)target);
|
|
*lent=0;
|
|
*target=NULL;
|
|
GB.Error("Unable to compress string");
|
|
return;
|
|
}
|
|
|
|
*lent = (uint)l;
|
|
}
|
|
/*********************************************************************************
|
|
The Driver must provide this function:
|
|
|
|
void u_String(char **target,unsigned int *lent,char *source,unsigned int len,int level)
|
|
|
|
It is called to decompress a String and return it decompressed. The object will
|
|
pass the following values:
|
|
|
|
target = NULL
|
|
lent = 0
|
|
source = a pointer to the original string
|
|
len = length of the original string
|
|
level = compression level
|
|
|
|
You will never receive a zero lenght string, nor a erroneus 'level' value
|
|
|
|
The function must store the decompressed string in 'target', and its length in 'lent',
|
|
or NULL and zero if it fails by any reason
|
|
*********************************************************************************/
|
|
static void u_String(char **target,unsigned int *lent,char *source,unsigned int len)
|
|
{
|
|
int res, try_gzip = 0;
|
|
z_stream stream = {
|
|
.zalloc = Z_NULL,
|
|
.zfree = Z_NULL,
|
|
.opaque = Z_NULL,
|
|
.avail_in = len,
|
|
.next_in = (Bytef *) source,
|
|
};
|
|
unsigned long pos = 0;
|
|
|
|
*lent = 2 * len;
|
|
GB.Alloc((void **) target, *lent);
|
|
|
|
stream.avail_out = *lent;
|
|
stream.next_out = (Bytef *) *target;
|
|
|
|
inflateInit(&stream);
|
|
while (1) {
|
|
res = inflate(&stream, Z_NO_FLUSH);
|
|
switch (res) {
|
|
case Z_OK:
|
|
break;
|
|
case Z_BUF_ERROR:
|
|
pos = (unsigned long) (stream.next_out - (unsigned long) *target);
|
|
*lent += *lent / 2;
|
|
GB.Realloc((void **) target, *lent);
|
|
stream.avail_out = *lent - pos;
|
|
stream.next_out = (Bytef *) (*target + pos);
|
|
break;
|
|
case Z_STREAM_END:
|
|
pos = (unsigned long) (stream.next_out - (unsigned long) *target);
|
|
goto out;
|
|
case Z_DATA_ERROR:
|
|
/* Maybe we have gzip format? */
|
|
if (try_gzip) {
|
|
GB.Error("Invalid compressed string");
|
|
goto error;
|
|
}
|
|
inflateEnd(&stream);
|
|
stream.avail_in = len;
|
|
stream.next_in = (Bytef *) source;
|
|
stream.avail_out = *lent;
|
|
stream.next_out = (Bytef *) *target;
|
|
inflateInit2(&stream, 16 + MAX_WBITS);
|
|
try_gzip = 1;
|
|
continue;
|
|
case Z_MEM_ERROR:
|
|
GB.Error("Not enough memory: string too long");
|
|
goto error;
|
|
default:
|
|
GB.Error("Unable to inflate string");
|
|
goto error;
|
|
}
|
|
}
|
|
out:
|
|
inflateEnd(&stream);
|
|
*lent = pos;
|
|
GB.Realloc((void **) target, *lent);
|
|
return;
|
|
|
|
error:
|
|
*lent = 0;
|
|
GB.Free((void **) target);
|
|
}
|
|
|
|
/*********************************************************************************
|
|
The Driver must provide this function:
|
|
|
|
static void c_File(char *source,char *target,int level)
|
|
|
|
It is called to compress a file. The object will pass the following values:
|
|
|
|
|
|
source = path of the file to be compressed
|
|
target = path of the new compressed file to create
|
|
level = compression level
|
|
|
|
You will never receive a erroneus 'level' value
|
|
*********************************************************************************/
|
|
|
|
static void c_File(char *source,char *target,int level)
|
|
{
|
|
FILE *src;
|
|
gzFile dst;
|
|
size_t len;
|
|
char buf[GB_Z_BUFFER];
|
|
char bufmode[4]={'w','b',0,0};
|
|
|
|
if (level != Z_DEFAULT_COMPRESSION )
|
|
bufmode[2] = (char)(level + 48);
|
|
|
|
if ((src = fopen(source,"r")) == NULL)
|
|
{
|
|
GB.Error ("Unable to open file for reading");
|
|
return;
|
|
}
|
|
|
|
if ((dst = gzopen(target,bufmode)) == NULL)
|
|
{
|
|
fclose(src);
|
|
GB.Error ("Unable to open file for writing");
|
|
return;
|
|
}
|
|
|
|
while (!feof(src))
|
|
{
|
|
len = fread((void*)buf,sizeof(char),GB_Z_BUFFER,src);
|
|
|
|
if (len < GB_Z_BUFFER)
|
|
{
|
|
if (ferror(src))
|
|
{
|
|
fclose(src);
|
|
gzclose(dst);
|
|
GB.Error("Error while reading data");
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (len > 0)
|
|
{
|
|
if (gzwrite(dst, buf, len) == 0)
|
|
{
|
|
fclose(src);
|
|
gzclose(dst);
|
|
GB.Error("Error while writing data");
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
fclose(src);
|
|
gzflush(dst, Z_SYNC_FLUSH);
|
|
gzclose(dst);
|
|
}
|
|
|
|
/*********************************************************************************
|
|
The Driver must provide this function:
|
|
|
|
static void u_File(char *source,char *target,int level)
|
|
|
|
It is called to decompress a file. The object will pass the following values:
|
|
|
|
|
|
source = path of the file to be decompressed
|
|
target = path of the new decompressed file to create
|
|
level = compression level
|
|
|
|
You will never receive a erroneus 'level' value
|
|
*********************************************************************************/
|
|
static void u_File(char *source,char *target)
|
|
{
|
|
gzFile src;
|
|
FILE *dst;
|
|
char buf[GB_Z_BUFFER];
|
|
unsigned int len;
|
|
|
|
if ( (src=gzopen(source,"rb"))==NULL)
|
|
{
|
|
GB.Error ("Unable to open file for reading");
|
|
return;
|
|
}
|
|
|
|
if ( (dst=fopen(target,"w"))==NULL)
|
|
{
|
|
gzclose(src);
|
|
GB.Error ("Unable to open file for writing");
|
|
return;
|
|
}
|
|
|
|
while (!gzeof(src))
|
|
{
|
|
len=gzread(src,buf,sizeof(char)*GB_Z_BUFFER);
|
|
if (len==-1)
|
|
{
|
|
fclose(dst);
|
|
gzclose(src);
|
|
GB.Error("Error while reading data");
|
|
return;
|
|
}
|
|
if (len)
|
|
{
|
|
if (len != fwrite((void*)buf,sizeof(char),len,dst) )
|
|
{
|
|
fclose(dst);
|
|
gzclose(src);
|
|
GB.Error("Error while writing data");
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
fclose(dst);
|
|
gzclose(src);
|
|
}
|
|
|
|
static void c_Open(char *path,int level, STREAM_COMPRESS *stream)
|
|
{
|
|
|
|
char mode[4]={'w','b',0,0};
|
|
|
|
stream->base.desc=&ZStream;
|
|
if (level != Z_DEFAULT_COMPRESSION ) mode[2]=(char)(level+48);
|
|
stream->mode = MODE_WRITE;
|
|
stream->handle = gzopen(path,mode);
|
|
|
|
if (stream->handle) return;
|
|
|
|
stream->base.desc = NULL;
|
|
if ( errno == Z_MEM_ERROR )
|
|
{
|
|
GB.Error("Not enough memory to manage selected file");
|
|
return;
|
|
}
|
|
|
|
GB.Error("Unable to open selected file");
|
|
}
|
|
|
|
static void u_Open(char *path, STREAM_COMPRESS *stream)
|
|
{
|
|
char mode[3]={'r','b',0};
|
|
|
|
stream->base.desc = &ZStream;
|
|
stream->mode = MODE_READ;
|
|
stream->handle = gzopen(path, mode);
|
|
|
|
if (stream->handle) return;
|
|
|
|
stream->base.desc=NULL;
|
|
if ( errno == Z_MEM_ERROR )
|
|
{
|
|
GB.Error("Not enough memory to manage selected file");
|
|
return;
|
|
}
|
|
|
|
GB.Error("Unable to open selected file");
|
|
}
|
|
|
|
/*************************************************************************
|
|
Stream related stuff
|
|
**************************************************************************/
|
|
/* not allowed stream methods */
|
|
static int CZ_stream_lof(GB_STREAM *stream, int64_t *len){return -1;}
|
|
static int CZ_stream_seek(GB_STREAM *stream, int64_t offset, int whence){ return -1;}
|
|
static int CZ_stream_open(GB_STREAM *stream, const char *path, int mode, void *data){return -1;}
|
|
/* allowed stream methods */
|
|
static int CZ_stream_tell(GB_STREAM *stream, int64_t *npos)
|
|
{
|
|
STREAM_COMPRESS *s = (STREAM_COMPRESS *)stream;
|
|
*npos=gztell (s->handle);
|
|
if ((*npos)!=-1) return 0;
|
|
gzclose (s->handle);
|
|
stream->desc=NULL;
|
|
return -1;
|
|
}
|
|
|
|
static int CZ_stream_flush(GB_STREAM *stream)
|
|
{
|
|
gzflush(((STREAM_COMPRESS *)stream)->handle,Z_SYNC_FLUSH);
|
|
return 0;
|
|
}
|
|
|
|
int CZ_stream_close(GB_STREAM *stream)
|
|
{
|
|
gzclose (((STREAM_COMPRESS *)stream)->handle);
|
|
stream->desc=NULL;
|
|
return 0;
|
|
}
|
|
|
|
static int CZ_stream_write(GB_STREAM *stream, char *buffer, int len)
|
|
{
|
|
STREAM_COMPRESS *s = (STREAM_COMPRESS *)stream;
|
|
|
|
if (s->mode == MODE_READ)
|
|
return -1;
|
|
|
|
return gzwrite(s->handle, (voidp)buffer, (unsigned)len);
|
|
}
|
|
|
|
static int CZ_stream_eof(GB_STREAM *stream)
|
|
{
|
|
return gzeof (((STREAM_COMPRESS *)stream)->handle);
|
|
}
|
|
|
|
static int CZ_stream_read(GB_STREAM *stream, char *buffer, int len)
|
|
{
|
|
STREAM_COMPRESS *s = (STREAM_COMPRESS *)stream;
|
|
|
|
if (s->mode == MODE_WRITE)
|
|
return -1;
|
|
|
|
return gzread(s->handle, (voidp)buffer, (unsigned)len);
|
|
}
|
|
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
This array of functions defines what functions the compression component
|
|
must call to perform its actions
|
|
****************************************************************************/
|
|
static COMPRESS_DRIVER _driver =
|
|
{
|
|
"zlib",
|
|
|
|
(void*)max_compression,
|
|
(void*)min_compression,
|
|
(void*)default_compression,
|
|
|
|
{
|
|
(void*)c_String,
|
|
(void*)c_File,
|
|
(void*)c_Open,
|
|
(void*)CZ_stream_close,
|
|
},
|
|
|
|
{
|
|
(void*)u_String,
|
|
(void*)u_File,
|
|
(void*)u_Open,
|
|
(void*)CZ_stream_close
|
|
}
|
|
};
|
|
|
|
/*****************************************************************************
|
|
|
|
The component entry and exit functions.
|
|
|
|
*****************************************************************************/
|
|
|
|
int EXPORT GB_INIT(void)
|
|
{
|
|
/*************************************************************************
|
|
When this component is loaded by Gambas runtime, it 'informs' to the
|
|
compression component what functions must it call to perform its actions
|
|
*************************************************************************/
|
|
GB.GetInterface("gb.compress", COMPRESS_INTERFACE_VERSION, &COMPRESSION);
|
|
COMPRESSION.Register(&_driver);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void EXPORT GB_EXIT()
|
|
{
|
|
}
|