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); }
int main(int argc, char *argv[]) { enum {COMPRESS, DECOMPRESS} style; size_t len; int bits, cat, ch; char *p, newname[MAXPATHLEN]; cat = 0; if ((p = strrchr(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) { switch(style) { case COMPRESS: (void)compress("/dev/stdin", "/dev/stdout", bits); break; case DECOMPRESS: (void)decompress("/dev/stdin", "/dev/stdout", bits); break; } exit (eval); } if (cat == 1 && argc > 1) errx(1, "the -c option permits only a single file argument"); for (; *argv; ++argv) switch(style) { case COMPRESS: if (strcmp(*argv, "-") == 0) { compress("/dev/stdin", "/dev/stdout", bits); break; } else if (cat) { compress(*argv, "/dev/stdout", bits); break; } if ((p = strrchr(*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) { decompress("/dev/stdin", "/dev/stdout", bits); break; } len = strlen(*argv); if ((p = strrchr(*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); }