aboutsummaryrefslogtreecommitdiffstats
path: root/file_cmds/compress/compress.c
diff options
context:
space:
mode:
authorCameron Katri <me@cameronkatri.com>2021-05-09 14:20:58 -0400
committerCameron Katri <me@cameronkatri.com>2021-05-09 14:20:58 -0400
commit5fd83771641d15c418f747bd343ba6738d3875f7 (patch)
tree5abf0f78f680d9837dbd93d4d4c3933bb7509599 /file_cmds/compress/compress.c
downloadapple_cmds-5fd83771641d15c418f747bd343ba6738d3875f7.tar.gz
apple_cmds-5fd83771641d15c418f747bd343ba6738d3875f7.tar.zst
apple_cmds-5fd83771641d15c418f747bd343ba6738d3875f7.zip
Import macOS userland
adv_cmds-176 basic_cmds-55 bootstrap_cmds-116.100.1 developer_cmds-66 diskdev_cmds-667.40.1 doc_cmds-53.60.1 file_cmds-321.40.3 mail_cmds-35 misc_cmds-34 network_cmds-606.40.1 patch_cmds-17 remote_cmds-63 shell_cmds-216.60.1 system_cmds-880.60.2 text_cmds-106
Diffstat (limited to 'file_cmds/compress/compress.c')
-rw-r--r--file_cmds/compress/compress.c467
1 files changed, 467 insertions, 0 deletions
diff --git a/file_cmds/compress/compress.c b/file_cmds/compress/compress.c
new file mode 100644
index 0000000..26da7fa
--- /dev/null
+++ b/file_cmds/compress/compress.c
@@ -0,0 +1,467 @@
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+__used static const char copyright[] =
+"@(#) Copyright (c) 1992, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif
+
+#if 0
+#ifndef lint
+static char sccsid[] = "@(#)compress.c 8.2 (Berkeley) 1/7/94";
+#endif
+#endif
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/usr.bin/compress/compress.c,v 1.23 2010/12/11 08:32:16 joel Exp $");
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include <err.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <locale.h>
+
+#include "zopen.h"
+
+void compress(const char *, const char *, int);
+void cwarn(const char *, ...) __printflike(1, 2);
+void cwarnx(const char *, ...) __printflike(1, 2);
+void decompress(const char *, const char *, int);
+int permission(const char *);
+void setfile(const char *, struct stat *);
+void usage(int);
+
+int eval, force, verbose, cat;
+
+int
+main(int argc, char *argv[])
+{
+ enum {COMPRESS, DECOMPRESS} style;
+ size_t len;
+ int bits, ch;
+ char *p, newname[MAXPATHLEN];
+
+ if (argc < 1)
+ usage(1);
+ cat = 0;
+ if ((p = rindex(argv[0], '/')) == NULL)
+ p = argv[0];
+ else
+ ++p;
+ if (!strcmp(p, "uncompress"))
+ style = DECOMPRESS;
+ else if (!strcmp(p, "compress"))
+ style = COMPRESS;
+ else if (!strcmp(p, "zcat")) {
+ cat = 1;
+ style = DECOMPRESS;
+ } else
+ errx(1, "unknown program name");
+
+ bits = 0;
+ while ((ch = getopt(argc, argv, "b:cdfv")) != -1)
+ switch(ch) {
+ case 'b':
+ bits = strtol(optarg, &p, 10);
+ if (*p)
+ errx(1, "illegal bit count -- %s", optarg);
+ break;
+ case 'c':
+ cat = 1;
+ break;
+ case 'd': /* Backward compatible. */
+ style = DECOMPRESS;
+ break;
+ case 'f':
+ force = 1;
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ case '?':
+ default:
+ usage(style == COMPRESS);
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc == 0) {
+ cat = 1;
+ switch(style) {
+ case COMPRESS:
+ (void)compress("/dev/stdin", "/dev/stdout", bits);
+ break;
+ case DECOMPRESS:
+ (void)decompress("/dev/stdin", "/dev/stdout", bits);
+ break;
+ }
+ exit (eval);
+ }
+
+ /*
+ * The UNIX standard requires that `uncompress -c` be able to have multiple file parameters given.
+ */
+
+ for (; *argv; ++argv)
+ switch(style) {
+ case COMPRESS:
+ if (strcmp(*argv, "-") == 0) {
+ cat = 1;
+ compress("/dev/stdin", "/dev/stdout", bits);
+ break;
+ } else if (cat) {
+ compress(*argv, "/dev/stdout", bits);
+ break;
+ }
+ if ((p = rindex(*argv, '.')) != NULL &&
+ !strcmp(p, ".Z")) {
+ cwarnx("%s: name already has trailing .Z",
+ *argv);
+ break;
+ }
+ len = strlen(*argv);
+ if (len > sizeof(newname) - 3) {
+ cwarnx("%s: name too long", *argv);
+ break;
+ }
+ memmove(newname, *argv, len);
+ newname[len] = '.';
+ newname[len + 1] = 'Z';
+ newname[len + 2] = '\0';
+ compress(*argv, newname, bits);
+ break;
+ case DECOMPRESS:
+ if (strcmp(*argv, "-") == 0) {
+ cat = 1;
+ decompress("/dev/stdin", "/dev/stdout", bits);
+ break;
+ }
+ len = strlen(*argv);
+ if ((p = rindex(*argv, '.')) == NULL ||
+ strcmp(p, ".Z")) {
+ if (len > sizeof(newname) - 3) {
+ cwarnx("%s: name too long", *argv);
+ break;
+ }
+ memmove(newname, *argv, len);
+ newname[len] = '.';
+ newname[len + 1] = 'Z';
+ newname[len + 2] = '\0';
+ decompress(newname,
+ cat ? "/dev/stdout" : *argv, bits);
+ } else {
+ if (len - 2 > sizeof(newname) - 1) {
+ cwarnx("%s: name too long", *argv);
+ break;
+ }
+ memmove(newname, *argv, len - 2);
+ newname[len - 2] = '\0';
+ decompress(*argv,
+ cat ? "/dev/stdout" : newname, bits);
+ }
+ break;
+ }
+ exit (eval);
+}
+
+void
+compress(const char *in, const char *out, int bits)
+{
+ size_t nr;
+ struct stat isb, sb;
+ FILE *ifp = NULL, *ofp = NULL;
+ int exists, isreg, oreg;
+ u_char buf[1024];
+
+ exists = !stat(out, &sb);
+ if (!force && exists && S_ISREG(sb.st_mode) && !cat && !permission(out)) {
+ cwarnx("%s already exists", out);
+ return;
+ }
+ isreg = oreg = !exists || S_ISREG(sb.st_mode);
+
+ if ((ifp = fopen(in, "r")) == NULL) {
+ cwarn("%s", in);
+ return;
+ }
+ if (stat(in, &isb)) { /* DON'T FSTAT! */
+ cwarn("%s", in);
+ goto err;
+ }
+ if (!S_ISREG(isb.st_mode))
+ isreg = 0;
+
+ if ((ofp = zopen(out, "w", bits)) == NULL) {
+ cwarn("%s", out);
+ goto err;
+ }
+ while ((nr = fread(buf, 1, sizeof(buf), ifp)) != 0)
+ if (fwrite(buf, 1, nr, ofp) != nr) {
+ cwarn("%s", out);
+ goto err;
+ }
+
+ if (ferror(ifp) || fclose(ifp)) {
+ cwarn("%s", in);
+ goto err;
+ }
+ ifp = NULL;
+
+ if (fclose(ofp)) {
+ cwarn("%s", out);
+ goto err;
+ }
+ ofp = NULL;
+
+ if (!cat && isreg) {
+ if (stat(out, &sb)) {
+ cwarn("%s", out);
+ goto err;
+ }
+
+ if (!force && sb.st_size >= isb.st_size) {
+ if (verbose)
+ (void)fprintf(stderr, "%s: file would grow; left unmodified\n",
+ in);
+ eval = 2;
+ if (unlink(out))
+ cwarn("%s", out);
+ goto err;
+ }
+
+ setfile(out, &isb);
+
+ if (unlink(in))
+ cwarn("%s", in);
+
+ if (verbose) {
+ (void)fprintf(stderr, "%s: ", out);
+ if (isb.st_size > sb.st_size)
+ (void)fprintf(stderr, "%.0f%% compression\n",
+ ((float)sb.st_size / isb.st_size) * 100.0);
+ else
+ (void)fprintf(stderr, "%.0f%% expansion\n",
+ ((float)isb.st_size / sb.st_size) * 100.0);
+ }
+ }
+ return;
+
+err: if (ofp) {
+ if (!cat && oreg)
+ (void)unlink(out);
+ (void)fclose(ofp);
+ }
+ if (ifp)
+ (void)fclose(ifp);
+}
+
+void
+decompress(const char *in, const char *out, int bits)
+{
+ size_t nr;
+ struct stat sb;
+ FILE *ifp, *ofp;
+ int exists, isreg, oreg;
+ u_char buf[1024];
+
+ exists = !stat(out, &sb);
+ if (!force && exists && S_ISREG(sb.st_mode) && !cat && !permission(out)) {
+ cwarnx("%s already exists", out);
+ return;
+ }
+ isreg = oreg = !exists || S_ISREG(sb.st_mode);
+
+ ofp = NULL;
+ if ((ifp = zopen(in, "r", bits)) == NULL) {
+ cwarn("%s", in);
+ return;
+ }
+ if (stat(in, &sb)) {
+ cwarn("%s", in);
+ goto err;
+ }
+ if (!S_ISREG(sb.st_mode))
+ isreg = 0;
+
+ /*
+ * Try to read the first few uncompressed bytes from the input file
+ * before blindly truncating the output file.
+ */
+ if ((nr = fread(buf, 1, sizeof(buf), ifp)) == 0) {
+ cwarn("%s", in);
+ (void)fclose(ifp);
+ return;
+ }
+ if ((ofp = fopen(out, "w")) == NULL ||
+ (nr != 0 && fwrite(buf, 1, nr, ofp) != nr)) {
+ cwarn("%s", out);
+ (void)fclose(ifp);
+ return;
+ }
+
+ while ((nr = fread(buf, 1, sizeof(buf), ifp)) != 0)
+ if (fwrite(buf, 1, nr, ofp) != nr) {
+ cwarn("%s", out);
+ goto err;
+ }
+
+ if (ferror(ifp) || fclose(ifp)) {
+ cwarn("%s", in);
+ goto err;
+ }
+ ifp = NULL;
+
+ if (fclose(ofp)) {
+ cwarn("%s", out);
+ goto err;
+ }
+
+ if (!cat && isreg) {
+ setfile(out, &sb);
+
+ if (unlink(in))
+ cwarn("%s", in);
+ if (verbose) {
+ struct stat isb = sb;
+ stat(out, &sb);
+ (void)fprintf(stderr, "%s: ", out);
+ if (isb.st_size > sb.st_size)
+ (void)fprintf(stderr, "%.0f%% compression\n",
+ ((float)sb.st_size / isb.st_size) * 100.0);
+ else
+ (void)fprintf(stderr, "%.0f%% expansion\n",
+ ((float)isb.st_size / sb.st_size) * 100.0);
+ }
+ }
+ return;
+
+err: if (ofp) {
+ if (!cat && oreg)
+ (void)unlink(out);
+ (void)fclose(ofp);
+ }
+ if (ifp)
+ (void)fclose(ifp);
+}
+
+void
+setfile(const char *name, struct stat *fs)
+{
+ static struct timeval tv[2];
+
+ fs->st_mode &= S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO;
+
+ TIMESPEC_TO_TIMEVAL(&tv[0], &fs->st_atimespec);
+ TIMESPEC_TO_TIMEVAL(&tv[1], &fs->st_mtimespec);
+ if (utimes(name, tv))
+ cwarn("utimes: %s", name);
+
+ /*
+ * Changing the ownership probably won't succeed, unless we're root
+ * or POSIX_CHOWN_RESTRICTED is not set. Set uid/gid before setting
+ * the mode; current BSD behavior is to remove all setuid bits on
+ * chown. If chown fails, lose setuid/setgid bits.
+ */
+ if (chown(name, fs->st_uid, fs->st_gid)) {
+ if (errno != EPERM)
+ cwarn("chown: %s", name);
+ fs->st_mode &= ~(S_ISUID|S_ISGID);
+ }
+ if (chmod(name, fs->st_mode) && errno != ENOTSUP)
+ cwarn("chmod: %s", name);
+
+ if (chflags(name, fs->st_flags) && errno != ENOTSUP)
+ cwarn("chflags: %s", name);
+}
+
+int
+permission(const char *fname)
+{
+ int ch, first;
+ char resp[] = {'\0', '\0'};
+
+ if (!isatty(fileno(stderr)))
+ return (0);
+ (void)fprintf(stderr, "overwrite %s? ", fname);
+
+ /* Load user specified locale */
+ setlocale(LC_MESSAGES, "");
+
+ first = ch = getchar();
+ while (ch != '\n' && ch != EOF)
+ ch = getchar();
+
+ /* only care about first character */
+ resp[0] = first;
+
+ return (rpmatch(resp) == 1);
+}
+
+void
+usage(int iscompress)
+{
+ if (iscompress)
+ (void)fprintf(stderr,
+ "usage: compress [-cfv] [-b bits] [file ...]\n");
+ else
+ (void)fprintf(stderr,
+ "usage: uncompress [-cfv] [file ...]\n");
+ exit(1);
+}
+
+void
+cwarnx(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vwarnx(fmt, ap);
+ va_end(ap);
+ eval = 1;
+}
+
+void
+cwarn(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vwarn(fmt, ap);
+ va_end(ap);
+ eval = 1;
+}