mirror of https://github.com/vasi/pixz
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
155 lines
4.1 KiB
C
155 lines
4.1 KiB
C
#include "pixz.h"
|
|
|
|
#include <archive.h>
|
|
#include <archive_entry.h>
|
|
|
|
#include <sys/errno.h>
|
|
#include <string.h>
|
|
#include <libgen.h>
|
|
|
|
|
|
// Tar uses records of 512 bytes
|
|
#define CHUNKSIZE 512
|
|
|
|
|
|
typedef struct pixz_tar_index_record pixz_tar_index_record;
|
|
struct pixz_tar_index_record {
|
|
size_t offset;
|
|
char *name;
|
|
pixz_tar_index_record *next;
|
|
};
|
|
|
|
typedef struct {
|
|
pixz_tar_index_record *first;
|
|
pixz_tar_index_record *last;
|
|
} pixz_tar_index;
|
|
|
|
static pixz_tar_index *pixz_tar_index_new(void);
|
|
static void pixz_tar_index_add(pixz_tar_index *i, size_t offset, const char *name);
|
|
static void pixz_tar_index_dump(pixz_tar_index *i, FILE *out);
|
|
static void pixz_tar_index_free(pixz_tar_index *i);
|
|
static int pixz_tar_index_is_metadata(struct archive_entry *entry);
|
|
|
|
|
|
typedef struct {
|
|
FILE *file;
|
|
uint8_t buf[CHUNKSIZE];
|
|
} pixz_tar_input;
|
|
|
|
static int pixz_tar_input_open(struct archive *a, void *refp);
|
|
static int pixz_tar_input_close(struct archive *a, void *refp);
|
|
static ssize_t pixz_tar_input_read(struct archive *a, void *refp, const void **buf);
|
|
|
|
|
|
int main(void) {
|
|
pixz_tar_index *index = pixz_tar_index_new();
|
|
|
|
struct archive *a = archive_read_new();
|
|
archive_read_support_compression_none(a);
|
|
archive_read_support_format_tar(a);
|
|
|
|
pixz_tar_input input = { .file = stdin };
|
|
if (archive_read_open(a, &input, pixz_tar_input_open, pixz_tar_input_read,
|
|
pixz_tar_input_close) != ARCHIVE_OK)
|
|
pixz_die("Can't open archive\n");
|
|
|
|
int want_offset = 0;
|
|
size_t offset = 0;
|
|
while (1) {
|
|
struct archive_entry *entry;
|
|
int aerr = archive_read_next_header(a, &entry);
|
|
if (aerr == ARCHIVE_EOF)
|
|
break;
|
|
else if (aerr != ARCHIVE_OK)
|
|
pixz_die("Error reading header\n");
|
|
|
|
if (!pixz_tar_index_is_metadata(entry)) {
|
|
const char *name = archive_entry_pathname(entry);
|
|
pixz_tar_index_add(index, offset, name);
|
|
want_offset = 1;
|
|
}
|
|
|
|
if (archive_read_data_skip(a) != ARCHIVE_OK)
|
|
pixz_die("Error skipping data\n");
|
|
|
|
if (want_offset) {
|
|
offset = ftell(input.file);
|
|
want_offset = 0;
|
|
}
|
|
}
|
|
if (archive_read_finish(a) != ARCHIVE_OK)
|
|
pixz_die("Error finishing read\n");
|
|
|
|
pixz_tar_index_dump(index, stdout);
|
|
pixz_tar_index_free(index);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int pixz_tar_input_open(struct archive *a, void *refp) {
|
|
return ARCHIVE_OK;
|
|
}
|
|
|
|
static int pixz_tar_input_close(struct archive *a, void *refp) {
|
|
fclose(((pixz_tar_input*)refp)->file);
|
|
return ARCHIVE_OK;
|
|
}
|
|
|
|
static ssize_t pixz_tar_input_read(struct archive *a, void *refp, const void **buf) {
|
|
pixz_tar_input *input = (pixz_tar_input*)refp;
|
|
size_t rd = fread(input->buf, 1, CHUNKSIZE, input->file);
|
|
if (ferror(input->file)) {
|
|
archive_set_error(a, errno, "Read error");
|
|
return -1;
|
|
}
|
|
*buf = input->buf;
|
|
return rd;
|
|
}
|
|
|
|
|
|
static pixz_tar_index *pixz_tar_index_new(void) {
|
|
pixz_tar_index *i = malloc(sizeof(pixz_tar_index));
|
|
i->first = NULL;
|
|
i->last = NULL;
|
|
return i;
|
|
}
|
|
|
|
static void pixz_tar_index_add(pixz_tar_index *i, size_t offset, const char *name) {
|
|
pixz_tar_index_record *rec = malloc(sizeof(pixz_tar_index_record));
|
|
rec->next = NULL;
|
|
rec->name = strdup(name);
|
|
rec->offset = offset;
|
|
|
|
if (!i->first)
|
|
i->first = rec;
|
|
if (i->last)
|
|
i->last->next = rec;
|
|
i->last = rec;
|
|
}
|
|
|
|
static void pixz_tar_index_dump(pixz_tar_index *i, FILE *out) {
|
|
for (pixz_tar_index_record *rec = i->first; rec; rec = rec->next) {
|
|
fprintf(out, "%12zu %s\n", rec->offset, rec->name);
|
|
}
|
|
}
|
|
|
|
static void pixz_tar_index_free(pixz_tar_index *i) {
|
|
for (pixz_tar_index_record *rec = i->first; rec; rec = rec->next) {
|
|
free(rec->name);
|
|
free(rec);
|
|
}
|
|
free(i);
|
|
}
|
|
|
|
static int pixz_tar_index_is_metadata(struct archive_entry *entry) {
|
|
// FIXME: better copyfile detection?
|
|
|
|
const char *name = archive_entry_pathname(entry);
|
|
size_t i = strlen(name);
|
|
while (i != 0 && name[i - 1] != '/')
|
|
--i;
|
|
|
|
return strncmp(name + i, "._", 2) == 0;
|
|
}
|