2020-06-15 14:18:57 +00:00
|
|
|
/*-*- 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 │
|
|
|
|
│ │
|
2020-12-28 01:18:44 +00:00
|
|
|
│ Permission to use, copy, modify, and/or distribute this software for │
|
|
|
|
│ any purpose with or without fee is hereby granted, provided that the │
|
|
|
|
│ above copyright notice and this permission notice appear in all copies. │
|
2020-06-15 14:18:57 +00:00
|
|
|
│ │
|
2020-12-28 01:18:44 +00:00
|
|
|
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
|
|
|
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
|
|
|
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
|
|
|
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
|
|
|
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
|
|
|
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
|
|
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
|
|
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
2020-06-15 14:18:57 +00:00
|
|
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
2023-07-28 13:17:34 +00:00
|
|
|
#include "libc/assert.h"
|
2020-06-15 14:18:57 +00:00
|
|
|
#include "libc/calls/calls.h"
|
|
|
|
#include "libc/calls/struct/stat.h"
|
2021-09-05 08:20:03 +00:00
|
|
|
#include "libc/elf/def.h"
|
2021-01-28 03:34:02 +00:00
|
|
|
#include "libc/fmt/conv.h"
|
2023-06-18 12:39:31 +00:00
|
|
|
#include "libc/fmt/libgen.h"
|
2020-06-15 14:18:57 +00:00
|
|
|
#include "libc/limits.h"
|
|
|
|
#include "libc/log/check.h"
|
|
|
|
#include "libc/log/log.h"
|
2022-09-13 06:10:38 +00:00
|
|
|
#include "libc/mem/gc.internal.h"
|
2020-06-15 14:18:57 +00:00
|
|
|
#include "libc/runtime/runtime.h"
|
|
|
|
#include "libc/stdio/stdio.h"
|
2022-03-16 23:59:45 +00:00
|
|
|
#include "libc/sysv/consts/clock.h"
|
2020-06-15 14:18:57 +00:00
|
|
|
#include "libc/sysv/consts/ex.h"
|
|
|
|
#include "libc/sysv/consts/exit.h"
|
|
|
|
#include "libc/sysv/consts/map.h"
|
|
|
|
#include "libc/sysv/consts/o.h"
|
|
|
|
#include "libc/sysv/consts/prot.h"
|
2022-06-09 03:01:28 +00:00
|
|
|
#include "libc/sysv/consts/s.h"
|
2022-03-16 23:59:45 +00:00
|
|
|
#include "libc/time/time.h"
|
2020-06-15 14:18:57 +00:00
|
|
|
#include "libc/x/x.h"
|
2023-06-10 01:02:06 +00:00
|
|
|
#include "libc/zip.internal.h"
|
2023-07-03 02:57:43 +00:00
|
|
|
#include "third_party/getopt/getopt.internal.h"
|
2020-06-15 14:18:57 +00:00
|
|
|
#include "tool/build/lib/elfwriter.h"
|
2021-08-12 07:42:14 +00:00
|
|
|
#include "tool/build/lib/stripcomponents.h"
|
2020-06-15 14:18:57 +00:00
|
|
|
|
2021-08-12 07:42:14 +00:00
|
|
|
char *name_;
|
|
|
|
char *yoink_;
|
2020-06-15 14:18:57 +00:00
|
|
|
char *symbol_;
|
|
|
|
char *outpath_;
|
2021-08-12 07:42:14 +00:00
|
|
|
bool nocompress_;
|
|
|
|
bool basenamify_;
|
|
|
|
int strip_components_;
|
|
|
|
const char *path_prefix_;
|
2021-09-28 05:58:51 +00:00
|
|
|
struct timespec timestamp;
|
2020-06-15 14:18:57 +00:00
|
|
|
|
2023-07-03 09:47:05 +00:00
|
|
|
wontreturn void PrintUsage(int fd, int rc) {
|
|
|
|
tinyprint(fd, "\n\
|
2023-06-10 16:15:19 +00:00
|
|
|
NAME\n\
|
|
|
|
\n\
|
|
|
|
Cosmpolitan Zip File Compiler\n\
|
|
|
|
\n\
|
|
|
|
SYNOPSIS\n\
|
|
|
|
\n\
|
2023-07-03 09:47:05 +00:00
|
|
|
",
|
|
|
|
program_invocation_name, " [FLAGS] FILE...\n\
|
2023-06-10 16:15:19 +00:00
|
|
|
\n\
|
|
|
|
DESCRIPTION\n\
|
|
|
|
\n\
|
|
|
|
This program may be used to turn arbitrary files into .zip.o files\n\
|
|
|
|
which can be incrementally linked into binaries, without quadratic\n\
|
|
|
|
compression complexity.\n\
|
|
|
|
\n\
|
|
|
|
FLAGS\n\
|
|
|
|
\n\
|
|
|
|
-h show help\n\
|
|
|
|
-o PATH output path\n\
|
|
|
|
-0 disable compression\n\
|
|
|
|
-B basename-ify zip filename\n\
|
|
|
|
-N ZIPPATH zip filename (defaults to input arg)\n\
|
|
|
|
-P ZIPPATH prepend path zip filename using join\n\
|
|
|
|
-C INTEGER strips leading path components from zip filename\n\
|
|
|
|
-y SYMBOL generate yoink for symbol (default __zip_eocd)\n\
|
|
|
|
\n\
|
|
|
|
",
|
2023-07-03 09:47:05 +00:00
|
|
|
NULL);
|
2020-06-15 14:18:57 +00:00
|
|
|
exit(rc);
|
|
|
|
}
|
|
|
|
|
|
|
|
void GetOpts(int *argc, char ***argv) {
|
|
|
|
int opt;
|
2023-06-10 16:15:19 +00:00
|
|
|
yoink_ = "__zip_eocd";
|
|
|
|
while ((opt = getopt(*argc, *argv, "?0nhBN:C:P:o:s:y:")) != -1) {
|
2020-06-15 14:18:57 +00:00
|
|
|
switch (opt) {
|
|
|
|
case 'o':
|
|
|
|
outpath_ = optarg;
|
|
|
|
break;
|
2021-08-12 07:42:14 +00:00
|
|
|
case 'n':
|
|
|
|
exit(0);
|
2020-06-15 14:18:57 +00:00
|
|
|
case 's':
|
|
|
|
symbol_ = optarg;
|
|
|
|
break;
|
2020-06-21 07:10:11 +00:00
|
|
|
case 'y':
|
|
|
|
yoink_ = optarg;
|
|
|
|
break;
|
2021-08-12 07:42:14 +00:00
|
|
|
case 'N':
|
|
|
|
name_ = optarg;
|
|
|
|
break;
|
|
|
|
case 'P':
|
|
|
|
path_prefix_ = optarg;
|
|
|
|
break;
|
|
|
|
case 'C':
|
|
|
|
strip_components_ = atoi(optarg);
|
|
|
|
break;
|
|
|
|
case 'B':
|
|
|
|
basenamify_ = true;
|
|
|
|
break;
|
|
|
|
case '0':
|
|
|
|
nocompress_ = true;
|
|
|
|
break;
|
2020-06-15 14:18:57 +00:00
|
|
|
case '?':
|
|
|
|
case 'h':
|
2023-07-03 09:47:05 +00:00
|
|
|
PrintUsage(1, EXIT_SUCCESS);
|
2020-06-15 14:18:57 +00:00
|
|
|
default:
|
2023-07-03 09:47:05 +00:00
|
|
|
PrintUsage(2, EX_USAGE);
|
2020-06-15 14:18:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
*argc -= optind;
|
|
|
|
*argv += optind;
|
2023-06-10 16:15:19 +00:00
|
|
|
if (!outpath_) {
|
2023-07-03 09:47:05 +00:00
|
|
|
tinyprint(2,
|
|
|
|
"error: no output path specified\n"
|
|
|
|
"run ",
|
|
|
|
program_invocation_name, " -h for usage\n", NULL);
|
2023-06-10 16:15:19 +00:00
|
|
|
exit(1);
|
|
|
|
}
|
2020-06-15 14:18:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void ProcessFile(struct ElfWriter *elf, const char *path) {
|
|
|
|
int fd;
|
|
|
|
void *map;
|
2020-08-25 11:23:25 +00:00
|
|
|
size_t pathlen;
|
2020-06-15 14:18:57 +00:00
|
|
|
struct stat st;
|
2021-08-12 07:42:14 +00:00
|
|
|
const char *name;
|
2023-07-28 13:17:34 +00:00
|
|
|
if (stat(path, &st)) {
|
|
|
|
perror(path);
|
|
|
|
exit(1);
|
|
|
|
}
|
2021-08-12 07:42:14 +00:00
|
|
|
if (S_ISDIR(st.st_mode)) {
|
2023-07-28 13:17:34 +00:00
|
|
|
if ((fd = open(path, O_RDONLY | O_DIRECTORY)) == -1) {
|
|
|
|
perror(path);
|
|
|
|
exit(1);
|
|
|
|
}
|
2021-08-12 07:42:14 +00:00
|
|
|
map = "";
|
|
|
|
st.st_size = 0;
|
|
|
|
} else if (st.st_size) {
|
2023-07-28 13:17:34 +00:00
|
|
|
if ((fd = open(path, O_RDONLY)) == -1 ||
|
|
|
|
(map = mmap(0, st.st_size, PROT_READ, MAP_SHARED, fd, 0)) ==
|
|
|
|
MAP_FAILED) {
|
|
|
|
perror(path);
|
|
|
|
exit(1);
|
|
|
|
}
|
2020-08-25 11:23:25 +00:00
|
|
|
} else {
|
2023-07-28 13:17:34 +00:00
|
|
|
fd = -1;
|
|
|
|
map = 0;
|
2020-08-25 11:23:25 +00:00
|
|
|
}
|
2021-08-12 07:42:14 +00:00
|
|
|
if (name_) {
|
|
|
|
name = name_;
|
|
|
|
} else {
|
|
|
|
name = path;
|
|
|
|
if (basenamify_) name = basename(name);
|
|
|
|
name = StripComponents(name, strip_components_);
|
|
|
|
if (path_prefix_) name = gc(xjoinpaths(path_prefix_, name));
|
|
|
|
}
|
|
|
|
if (S_ISDIR(st.st_mode)) {
|
2021-09-28 05:58:51 +00:00
|
|
|
st.st_size = 0;
|
2022-09-13 06:10:38 +00:00
|
|
|
if (!_endswith(name, "/")) {
|
2022-05-25 18:31:08 +00:00
|
|
|
name = gc(xstrcat(name, '/'));
|
2021-08-12 07:42:14 +00:00
|
|
|
}
|
2020-10-11 04:18:53 +00:00
|
|
|
}
|
2021-09-04 22:44:00 +00:00
|
|
|
elfwriter_zip(elf, name, name, strlen(name), map, st.st_size, st.st_mode,
|
2023-06-10 16:15:19 +00:00
|
|
|
timestamp, timestamp, timestamp, nocompress_);
|
2023-07-28 13:17:34 +00:00
|
|
|
if (st.st_size) {
|
|
|
|
unassert(!munmap(map, st.st_size));
|
|
|
|
}
|
2021-08-12 07:42:14 +00:00
|
|
|
close(fd);
|
2020-06-15 14:18:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void PullEndOfCentralDirectoryIntoLinkage(struct ElfWriter *elf) {
|
|
|
|
elfwriter_align(elf, 1, 0);
|
2023-06-10 16:15:19 +00:00
|
|
|
elfwriter_startsection(elf, ".yoink", SHT_PROGBITS, SHF_EXECINSTR);
|
2021-09-05 08:20:03 +00:00
|
|
|
elfwriter_yoink(elf, yoink_, STB_GLOBAL);
|
2020-06-15 14:18:57 +00:00
|
|
|
elfwriter_finishsection(elf);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CheckFilenameKosher(const char *path) {
|
2023-06-10 16:15:19 +00:00
|
|
|
CHECK_LE(kZipCfileHdrMinSize + strlen(path), 65535);
|
2022-09-13 06:10:38 +00:00
|
|
|
CHECK(!_startswith(path, "/"));
|
2020-06-15 14:18:57 +00:00
|
|
|
CHECK(!strstr(path, ".."));
|
|
|
|
}
|
|
|
|
|
|
|
|
void zipobj(int argc, char **argv) {
|
|
|
|
size_t i;
|
|
|
|
struct ElfWriter *elf;
|
|
|
|
CHECK_LT(argc, UINT16_MAX / 3 - 64); /* ELF 64k section limit */
|
|
|
|
GetOpts(&argc, &argv);
|
|
|
|
for (i = 0; i < argc; ++i) CheckFilenameKosher(argv[i]);
|
|
|
|
elf = elfwriter_open(outpath_, 0644);
|
|
|
|
elfwriter_cargoculting(elf);
|
|
|
|
for (i = 0; i < argc; ++i) ProcessFile(elf, argv[i]);
|
|
|
|
PullEndOfCentralDirectoryIntoLinkage(elf);
|
|
|
|
elfwriter_close(elf);
|
|
|
|
}
|
|
|
|
|
|
|
|
int main(int argc, char **argv) {
|
2022-03-16 23:59:45 +00:00
|
|
|
timestamp.tv_sec = 1647414000; /* determinism */
|
|
|
|
/* clock_gettime(CLOCK_REALTIME, ×tamp); */
|
2020-06-15 14:18:57 +00:00
|
|
|
zipobj(argc, argv);
|
|
|
|
return 0;
|
|
|
|
}
|