blob: cc21a57c2554317c5a6616357acdbb6c5cb9055a [file] [log] [blame]
#include <u.h>
#include <libc.h>
#include <bio.h>
#include "bzlib.h"
static int bzipf(char*, int);
static int bzip(char*, long, int, Biobuf*);
static Biobuf bout;
static int level;
static int debug;
static int verbose;
static void
usage(void)
{
fprint(2, "usage: bzip2 [-vcD] [-1-9] [file ...]\n");
exits("usage");
}
void
main(int argc, char **argv)
{
int i, ok, stdout;
char **oargv;
oargv = argv;
level = 6;
stdout = 0;
ARGBEGIN{
case 'D':
debug++;
break;
case 'v':
verbose++;
break;
case 'c':
stdout++;
break;
case 'f':
/* force */
break;
case 'd':
/*
* gnu tar expects bzip2 -d to decompress
* humor it. ugh.
*/
/* remove -d from command line - magic! */
if(strcmp(argv[0], "-d") == 0){
while(*argv++)
*(argv-1) = *argv;
}else
memmove(_args-1, _args, strlen(_args)+1);
exec("bunzip2", oargv);
sysfatal("exec bunzip2 failed");
break;
case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
level = ARGC() - '0';
break;
default:
usage();
break;
}ARGEND
if(argc == 0){
Binit(&bout, 1, OWRITE);
ok = bzip(nil, time(0), 0, &bout);
Bterm(&bout);
}else{
ok = 1;
for(i = 0; i < argc; i++)
ok &= bzipf(argv[i], stdout);
}
exits(ok ? nil: "errors");
}
static int
bzipf(char *file, int stdout)
{
Dir *dir;
char ofile[128], *f, *s;
int ifd, ofd, ok;
ifd = open(file, OREAD);
if(ifd < 0){
fprint(2, "bzip2: can't open %s: %r\n", file);
return 0;
}
dir = dirfstat(ifd);
if(dir == nil){
fprint(2, "bzip2: can't stat %s: %r\n", file);
close(ifd);
return 0;
}
if(dir->mode & DMDIR){
fprint(2, "bzip2: can't compress a directory\n");
close(ifd);
free(dir);
return 0;
}
if(stdout){
ofd = 1;
strcpy(ofile, "<stdout>");
}else{
f = strrchr(file, '/');
if(f != nil)
f++;
else
f = file;
s = strrchr(f, '.');
if(s != nil && s != ofile && strcmp(s, ".tar") == 0){
*s = '\0';
snprint(ofile, sizeof(ofile), "%s.tbz", f);
}else
snprint(ofile, sizeof(ofile), "%s.bz2", f);
ofd = create(ofile, OWRITE, 0666);
if(ofd < 0){
fprint(2, "bzip2: can't open %s: %r\n", ofile);
free(dir);
close(ifd);
return 0;
}
}
if(verbose)
fprint(2, "compressing %s to %s\n", file, ofile);
Binit(&bout, ofd, OWRITE);
ok = bzip(file, dir->mtime, ifd, &bout);
if(!ok || Bflush(&bout) < 0){
fprint(2, "bzip2: error writing %s: %r\n", ofile);
if(!stdout)
remove(ofile);
}
Bterm(&bout);
free(dir);
close(ifd);
close(ofd);
return ok;
}
static int
bzip(char *file, long mtime, int ifd, Biobuf *bout)
{
int e, n, done, onemore;
char buf[8192];
char obuf[8192];
Biobuf bin;
bz_stream strm;
USED(file);
USED(mtime);
memset(&strm, 0, sizeof strm);
BZ2_bzCompressInit(&strm, level, verbose, 0);
strm.next_in = buf;
strm.avail_in = 0;
strm.next_out = obuf;
strm.avail_out = sizeof obuf;
done = 0;
Binit(&bin, ifd, OREAD);
/*
* onemore is a crummy hack to go 'round the loop
* once after we finish, to flush the output buffer.
*/
onemore = 1;
SET(e);
do {
if(!done && strm.avail_in < sizeof buf) {
if(strm.avail_in)
memmove(buf, strm.next_in, strm.avail_in);
n = Bread(&bin, buf+strm.avail_in, sizeof(buf)-strm.avail_in);
if(n <= 0)
done = 1;
else
strm.avail_in += n;
strm.next_in = buf;
}
if(strm.avail_out < sizeof obuf) {
Bwrite(bout, obuf, sizeof(obuf)-strm.avail_out);
strm.next_out = obuf;
strm.avail_out = sizeof obuf;
}
if(onemore == 0)
break;
} while((e=BZ2_bzCompress(&strm, done ? BZ_FINISH : BZ_RUN)) == BZ_RUN_OK || e == BZ_FINISH_OK || onemore--);
if(e != BZ_STREAM_END) {
fprint(2, "bzip2: compress failed\n");
return 0;
}
if(BZ2_bzCompressEnd(&strm) != BZ_OK) {
fprint(2, "bzip2: compress end failed (can't happen)\n");
return 0;
}
Bterm(&bin);
return 1;
}