| #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; | 
 | } |