mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-05-24 06:12:27 +00:00
Initial import
This commit is contained in:
commit
c91b3c5006
14915 changed files with 590219 additions and 0 deletions
243
tool/build/rle.c
Normal file
243
tool/build/rle.c
Normal file
|
@ -0,0 +1,243 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ 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; version 2 of the License. │
|
||||
│ │
|
||||
│ 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 │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/fmt/fmt.h"
|
||||
#include "libc/log/check.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/ex.h"
|
||||
#include "libc/sysv/consts/exit.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "third_party/getopt/getopt.h"
|
||||
|
||||
#define USAGE1 \
|
||||
"NAME\n\
|
||||
\n\
|
||||
rle - Run Length Encoder\n\
|
||||
\n\
|
||||
SYNOPSIS\n\
|
||||
\n\
|
||||
"
|
||||
|
||||
#define USAGE2 \
|
||||
" [FLAGS] [FILE...]\n\
|
||||
\n\
|
||||
DESCRIPTION\n\
|
||||
\n\
|
||||
This is a primitive compression algorithm. Its advantage is that\n\
|
||||
the concomitant rldecode() library is seventeen bytes, and works\n\
|
||||
on IA-16, IA-32, and NexGen32e without needing to be recompiled.\n\
|
||||
\n\
|
||||
This CLI is consistent with gzip, bzip2, lzma, etc.\n\
|
||||
\n\
|
||||
FLAGS\n\
|
||||
\n\
|
||||
-1 .. -9 ignored\n\
|
||||
-a ignored\n\
|
||||
-c send to stdout\n\
|
||||
-d decompress\n\
|
||||
-f ignored\n\
|
||||
-t test integrity\n\
|
||||
-S SUFFIX overrides .rle extension\n\
|
||||
-h shows this information\n"
|
||||
|
||||
FILE *fin_, *fout_;
|
||||
bool decompress_, test_;
|
||||
const char *suffix_, *hint_;
|
||||
|
||||
void StartErrorMessage(void) {
|
||||
fputs("error: ", stderr);
|
||||
fputs(hint_, stderr);
|
||||
fputs(": ", stderr);
|
||||
}
|
||||
|
||||
void PrintIoErrorMessage(void) {
|
||||
int err;
|
||||
err = errno;
|
||||
StartErrorMessage();
|
||||
fputs(strerror(err), stderr);
|
||||
fputc('\n', stderr);
|
||||
}
|
||||
|
||||
void PrintUsage(int rc, FILE *f) {
|
||||
fputs(USAGE1, f);
|
||||
fputs(program_invocation_name, f);
|
||||
fputs(USAGE2, f);
|
||||
exit(rc);
|
||||
}
|
||||
|
||||
void GetOpts(int argc, char *argv[]) {
|
||||
int opt;
|
||||
fin_ = stdin;
|
||||
suffix_ = ".rle";
|
||||
while ((opt = getopt(argc, argv, "123456789S:acdfho:t")) != -1) {
|
||||
switch (opt) {
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
case '8':
|
||||
case '9':
|
||||
case 'a':
|
||||
case 'f':
|
||||
break;
|
||||
case 'c':
|
||||
fout_ = stdout;
|
||||
break;
|
||||
case 'd':
|
||||
decompress_ = true;
|
||||
break;
|
||||
case 't':
|
||||
test_ = true;
|
||||
break;
|
||||
case 'o':
|
||||
fclose_s(&fout_);
|
||||
if (!(fout_ = fopen((hint_ = optarg), "w"))) {
|
||||
PrintIoErrorMessage();
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
case 'S':
|
||||
suffix_ = optarg;
|
||||
break;
|
||||
case 'h':
|
||||
case '?':
|
||||
PrintUsage(EXIT_SUCCESS, stdout);
|
||||
default:
|
||||
PrintUsage(EX_USAGE, stderr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int RunLengthEncode1(void) {
|
||||
int byte1, byte2, runlength;
|
||||
byte2 = -1;
|
||||
runlength = 0;
|
||||
if ((byte1 = fgetc(fin_)) == -1) return -1;
|
||||
do {
|
||||
while (++runlength < 255) {
|
||||
if ((byte2 = fgetc(fin_)) != byte1) break;
|
||||
}
|
||||
if (fputc(runlength, fout_) == -1 || fputc(byte1, fout_) == -1) {
|
||||
return -1;
|
||||
}
|
||||
runlength = 0;
|
||||
} while ((byte1 = byte2) != -1);
|
||||
return feof(fin_) ? 0 : -1;
|
||||
}
|
||||
|
||||
int RunLengthEncode2(void) { return fputc(0, fout_) | fputc(0, fout_); }
|
||||
|
||||
int EmitRun(unsigned char count, unsigned char byte) {
|
||||
do {
|
||||
if (fputc(byte, fout_) == -1) return -1;
|
||||
} while (--count);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int RunLengthDecode(void) {
|
||||
int byte1, byte2;
|
||||
if ((byte1 = fgetc(fin_)) == -1) return einval();
|
||||
if ((byte2 = fgetc(fin_)) == -1) return einval();
|
||||
while (byte1) {
|
||||
if (!test_ && EmitRun(byte1, byte2) == -1) return -1;
|
||||
if ((byte1 = fgetc(fin_)) == -1) break;
|
||||
if ((byte2 = fgetc(fin_)) == -1) return einval();
|
||||
}
|
||||
if (byte1 != 0 || byte2 != 0) return einval();
|
||||
fgetc(fin_);
|
||||
return feof(fin_) ? 0 : -1;
|
||||
}
|
||||
|
||||
int RunLengthCode(void) {
|
||||
if (test_ || decompress_) {
|
||||
return RunLengthDecode();
|
||||
} else {
|
||||
return RunLengthEncode1();
|
||||
}
|
||||
}
|
||||
|
||||
int Run(char **paths, size_t count) {
|
||||
int rc;
|
||||
char *p;
|
||||
size_t i, suffixlen;
|
||||
const char pathbuf[PATH_MAX];
|
||||
if (!count) {
|
||||
hint_ = "/dev/stdin";
|
||||
if (!fout_) fout_ = stdout;
|
||||
rc = RunLengthCode();
|
||||
rc |= fclose_s(&fin_);
|
||||
} else {
|
||||
rc = fclose_s(&fin_);
|
||||
for (i = 0; i < count && rc != -1; ++i) {
|
||||
rc = -1;
|
||||
if ((fin_ = fopen((hint_ = paths[i]), "r"))) {
|
||||
if (test_ || fout_) {
|
||||
rc = RunLengthCode();
|
||||
} else {
|
||||
suffixlen = strlen(suffix_);
|
||||
if (!IsTrustworthy() && strlen(paths[i]) + suffixlen + 1 > PATH_MAX) {
|
||||
return eoverflow();
|
||||
}
|
||||
p = stpcpy(pathbuf, paths[i]);
|
||||
if (!decompress_) {
|
||||
strcpy(p, suffix_);
|
||||
} else if (p - pathbuf > suffixlen &&
|
||||
memcmp(p - suffixlen, suffix_, suffixlen) == 0) {
|
||||
p[-suffixlen] = '\0';
|
||||
} else {
|
||||
return enotsup();
|
||||
}
|
||||
if ((fout_ = fopen((hint_ = pathbuf), "w"))) {
|
||||
rc = RunLengthCode();
|
||||
if (rc != -1 && !decompress_) {
|
||||
rc = RunLengthEncode2();
|
||||
}
|
||||
if ((rc |= fclose_s(&fout_)) != -1) {
|
||||
unlink(paths[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
rc |= fclose_s(&fin_);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (rc != -1 && fout_) {
|
||||
rc = RunLengthEncode2();
|
||||
rc |= fclose_s(&fout_);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
GetOpts(argc, argv);
|
||||
if (Run(argv + optind, argc - optind) != -1) {
|
||||
return EXIT_SUCCESS;
|
||||
} else {
|
||||
PrintIoErrorMessage();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue