Add untar

This commit is contained in:
luboslenco
2026-01-08 15:41:01 +01:00
parent 822f2b87b8
commit 76b79a6cc8
4 changed files with 328 additions and 0 deletions
+1
View File
@@ -284,6 +284,7 @@ if (flags.with_nfd && (platform == "windows" || platform == "linux" || platform
if (flags.with_compress) {
project.add_define("WITH_COMPRESS");
project.add_cfiles("sources/libs/untar.c");
}
if (flags.with_image_write) {
+1
View File
@@ -335,6 +335,7 @@ string_t *iron_get_arg(i32 index) {
#ifdef WITH_COMPRESS
unsigned char *iron_deflate_raw(unsigned char *data, int data_len, int *out_len, int quality);
#define STBIW_ZLIB_COMPRESS iron_deflate_raw
void untar_here(const char *path);
#endif
#define STBI_WINDOWS_UTF8
#define STB_IMAGE_WRITE_IMPLEMENTATION
+318
View File
@@ -0,0 +1,318 @@
/*
* This file is in the public domain. Use it as you see fit.
* Originally written by Tim Kientzle, March 2009.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#if !defined(_WIN32)
#include <unistd.h>
#endif
#if defined(_WIN32)
#include <direct.h>
#endif
#define BLOCKSIZE 512
#define MAX_PATH_LEN 1024
static int system_mkdir(char *pathname, int mode) {
#if defined(_WIN32)
(void)mode; /* UNUSED */
return _mkdir(pathname);
#else
return mkdir(pathname, mode);
#endif
}
/* Parse an octal number, ignoring leading and trailing nonsense. */
static size_t parseoct(const char *p, size_t n) {
size_t i = 0;
while ((*p < '0' || *p > '7') && n > 0) {
++p;
--n;
}
while (*p >= '0' && *p <= '7' && n > 0) {
i *= 8;
i += *p - '0';
++p;
--n;
}
return (i);
}
/* Returns true if this is 512 zero bytes. */
static int is_end_of_archive(const char *p) {
int n;
for (n = 0; n < BLOCKSIZE; ++n)
if (p[n] != '\0')
return (0);
return (1);
}
/* Get the directory name from the path. */
static void get_dirname(const char *path, char *dir, size_t dir_size) {
strncpy(dir, path, dir_size - 1);
dir[dir_size - 1] = '\0';
char *last_slash = NULL;
char *q = dir;
while (*q) {
if (*q == '/' || *q == '\\') {
last_slash = q;
}
q++;
}
if (last_slash != NULL) {
*last_slash = '\0';
}
else {
strcpy(dir, ".");
}
}
/* Create a directory using the full path, including parent directories as necessary. */
static void create_dir_full(char *fullpath, int mode) {
char *p;
int r;
/* Strip trailing '/' */
if (fullpath[strlen(fullpath) - 1] == '/')
fullpath[strlen(fullpath) - 1] = '\0';
/* Try creating the directory. */
r = system_mkdir(fullpath, mode);
if (r != 0) {
/* On failure, try creating parent directory. */
p = strrchr(fullpath, '/');
if (p == NULL) {
#if defined(_WIN32)
p = strrchr(fullpath, '\\');
#endif
}
if (p != NULL && p != fullpath) {
*p = '\0';
create_dir_full(fullpath, 0755);
*p = '/';
r = system_mkdir(fullpath, mode);
}
}
if (r != 0)
fprintf(stderr, "Could not create directory %s\n", fullpath);
}
/* Create a file using the full path, including parent directory as necessary. */
static FILE *create_file_full(char *fullpath, int mode) {
FILE *f;
f = fopen(fullpath, "wb+");
if (f == NULL) {
/* Try creating parent dir and then creating file. */
char *p = strrchr(fullpath, '/');
if (p == NULL) {
#if defined(_WIN32)
p = strrchr(fullpath, '\\');
#endif
}
if (p != NULL) {
*p = '\0';
create_dir_full(fullpath, 0755);
*p = '/';
f = fopen(fullpath, "wb+");
}
}
return (f);
}
/* Verify the tar checksum. */
static int verify_checksum(const char *p) {
int n, u = 0;
for (n = 0; n < BLOCKSIZE; ++n) {
if (n < 148 || n > 155)
/* Standard tar checksum adds unsigned bytes. */
u += ((unsigned char *)p)[n];
else
u += 0x20;
}
return (u == (int)parseoct(p + 148, 8));
}
/* Extract a tar archive. */
static void untar(FILE *a, const char *path) {
char buff[BLOCKSIZE];
char extract_dir[MAX_PATH_LEN];
char fullpath[MAX_PATH_LEN];
FILE *f = NULL;
size_t bytes_read;
size_t filesize;
char *override_name = NULL;
char *override_link = NULL;
get_dirname(path, extract_dir, sizeof(extract_dir));
for (;;) {
bytes_read = fread(buff, 1, BLOCKSIZE, a);
if (bytes_read < BLOCKSIZE) {
fprintf(stderr, "Short read on %s: expected %d, got %d\n", path, BLOCKSIZE, (int)bytes_read);
if (override_name)
free(override_name);
if (override_link)
free(override_link);
return;
}
if (is_end_of_archive(buff)) {
if (override_name)
free(override_name);
if (override_link)
free(override_link);
return;
}
if (!verify_checksum(buff)) {
fprintf(stderr, "Checksum failure\n");
if (override_name)
free(override_name);
if (override_link)
free(override_link);
return;
}
char entry_name[MAX_PATH_LEN];
entry_name[0] = '\0';
char link_name[101];
link_name[0] = '\0';
char typeflag = buff[156];
filesize = parseoct(buff + 124, 12);
/* Handle override name from previous 'L' or 'K' */
if (override_name) {
strncpy(entry_name, override_name, MAX_PATH_LEN - 1);
entry_name[MAX_PATH_LEN - 1] = '\0';
free(override_name);
override_name = NULL;
}
else {
char short_name[101];
memcpy(short_name, buff, 100);
short_name[100] = '\0';
strcpy(entry_name, short_name);
/* Handle ustar prefix */
if (memcmp(buff + 257, "ustar", 5) == 0) {
char prefix[156];
memcpy(prefix, buff + 345, 155);
prefix[155] = '\0';
if (prefix[0] != '\0') {
strcpy(entry_name, prefix);
strcat(entry_name, "/");
strcat(entry_name, short_name);
}
}
}
if (override_link) {
strncpy(link_name, override_link, sizeof(link_name) - 1);
link_name[sizeof(link_name) - 1] = '\0';
free(override_link);
override_link = NULL;
}
else {
memcpy(link_name, buff + 157, 100);
link_name[100] = '\0';
}
/* Build fullpath */
strcpy(fullpath, extract_dir);
if (strcmp(extract_dir, ".") != 0 && extract_dir[0] != '\0') {
strcat(fullpath, "/");
}
strcat(fullpath, entry_name);
switch (typeflag) {
case 'L': /* GNU long name */
{
char *longname = (char *)malloc(filesize + 1);
char *ptr = longname;
size_t remaining = filesize;
while (remaining > 0) {
bytes_read = fread(buff, 1, BLOCKSIZE, a);
if (bytes_read < BLOCKSIZE && remaining >= BLOCKSIZE) {
fprintf(stderr, "Short read on longname: expected %d, got %d\n", BLOCKSIZE, (int)bytes_read);
free(longname);
return;
}
size_t to_copy = (remaining < BLOCKSIZE) ? (size_t)remaining : BLOCKSIZE;
memcpy(ptr, buff, to_copy);
ptr += to_copy;
remaining -= to_copy;
}
*ptr = '\0';
/* Find the actual string length */
longname[strnlen(longname, filesize)] = '\0';
override_name = longname;
continue; /* Skip data writing loop */
}
case '1': /* Hardlink */
filesize = 0;
break;
case '2': /* Symlink */
filesize = 0;
break;
case '3':
filesize = 0;
break;
case '4':
filesize = 0;
break;
case '5':
create_dir_full(fullpath, (int)parseoct(buff + 100, 8));
filesize = 0;
break;
case '6':
filesize = 0;
break;
default:
f = create_file_full(fullpath, (int)parseoct(buff + 100, 8));
break;
}
while (filesize > 0) {
bytes_read = fread(buff, 1, BLOCKSIZE, a);
if (bytes_read < BLOCKSIZE) {
fprintf(stderr, "Short read on %s: Expected %d, got %d\n", path, BLOCKSIZE, (int)bytes_read);
if (f != NULL)
fclose(f);
if (override_name)
free(override_name);
if (override_link)
free(override_link);
return;
}
if (filesize < BLOCKSIZE)
bytes_read = (size_t)filesize;
if (f != NULL) {
if (fwrite(buff, 1, bytes_read, f) != bytes_read) {
fprintf(stderr, "Failed write to %s\n", fullpath);
fclose(f);
f = NULL;
}
}
filesize -= bytes_read;
}
if (f != NULL) {
fclose(f);
f = NULL;
}
}
}
void untar_here(const char *path) {
FILE *a = fopen(path, "rb");
if (a == NULL)
fprintf(stderr, "Unable to open %s\n", path);
else {
untar(a, path);
fclose(a);
}
}
@@ -135,6 +135,14 @@ function neural_node_download(url: string) {
iron_sys_command("chmod +x \"" + bin + "\"");
}
/// end
if (ends_with(url, "Hunyuan3D_win64.tar")) {
console_toast(tr("Unpacking Hunyuan3D_win64.tar"));
sys_notify_on_next_frame(function() {
let tar: string = neural_node_dir() + "/Hunyuan3D_win64.tar";
untar_here(tar);
});
}
});
}