In C I have created a program which can archive multiple files into an archive file via the command line.
e.g.
$echo 'file1/2' > file1/2.txt

$./archive file1.txt file2.txt arhivedfile

$cat archivedfile
file1
file2

how do I create a process so that in my archivedfile I have:
header
file1
end
header
file2
end


They are all stored in the archive file one after another after another. I know that perhaps a header file is needed(containing filename, size of filename, start and end of file) for extracting these files back out into their original form, but how would I go about doing this.

I am stuck on where and how to start
Please could someone help me on some logic as to how to approach extracting files back out of an archived file.

Dani AI

Generated

Building on ’s and ’s posts, pick an archive topology first — that decision guides the rest of the logic. Three practical approaches: (A) build a table-of-contents up front (two-pass), (B) write per-file placeholders and patch sizes later with fseek, or (C) stream file data and append a central directory at the end (single-pass streaming + random access). Each has tradeoffs for memory, simplicity, and extraction speed.

A compact, practical on-disk layout to consider:

ARCHIVE (binary):
  MAGIC[8]  VERSION[1]

  file-data...

  central-directory:
    num_entries (4)
    for each entry:
      name_len (2)  name (bytes)
      size (8)  offset (8)  crc32 (4)  mode(4)  mtime(8)

This lets writers stream data once, collect offsets/sizes in memory, then write a small central directory. Readers verify the magic/version and use the directory to extract files quickly.

Extraction checklist and safe-practices:

  • Verify archive magic and version before trusting contents.
  • Sanitize stored paths (strip leading slashes, collapse or reject "..") so extraction cannot escape the target directory.
  • Create directories before opening output files. Implement an overwrite policy (skip/rename/replace).
  • Copy exactly the declared number of bytes per file using a buffered loop and check return counts from fread/fwrite; on mismatch abort and cleanup.
  • Optionally verify crc32/sha checksum and restore stored metadata (mtime, mode) if present.

Implementation cautions and tips:

  • Do not fwrite raw C structs to disk: struct padding and endianness break portability. Serialize fields with fixed-width types (uint16_t/uint32_t/uint64_t) and a chosen byte order.
  • Use 64-bit lengths for large files and fseeko/ftello or off_t where available.
  • Check every I/O return value; test with binary data and filenames containing spaces and odd chars.
  • Add a format version in the magic so future changes remain compatible.

These choices give a robust, secure extraction flow while keeping the archive human-manageable and extensible.

Recommended Answers

All 3 Replies

Program archive will most likely have to make two passes through each of the command line arguments. The first pass will collect a list of file names so that it can build a list of of those names along with their file sizes. Once all that information has been collected store it as the header in the result output file. After thant make a second pass through the arguments but this time actually load each of the files and copy them to the output file.

You will want to read the input files and write the output file in binary mode instead of text mode so that the output file can contain any mixture of text and binary files.

I know that perhaps a header file is needed(containing filename, size of filename, start and end of file) for extracting these files back out into their original form, but how would I go about doing this.

Here's a simple example where each file is preceded by a header with the name and size. Please excuse any errors or issues, it was written in haste:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#define ARCHIVE "-a"
#define EXTRACT "-e"
#define MAX_NAME 512
#define BLOCK_SIZE 512

struct header {
    char name[MAX_NAME];
    size_t size;
};

static void add_to_archive(FILE *archive, FILE *src, const char *src_name)
{
    struct header hdr;
    char block[BLOCK_SIZE];
    size_t n;

    fseek(src, 0, SEEK_END);
    strcpy(hdr.name, src_name);
    hdr.size = ftell(src);
    fseek(src, 0, SEEK_SET);

    fwrite(&hdr, sizeof hdr, 1, archive);
    
    while ((n = fread(block, 1, sizeof block, src)) > 0)
        fwrite(block, 1, n, archive);
}

static void extract_all(FILE *archive)
{
    struct header hdr;

    while (fread(&hdr, sizeof hdr, 1, archive) > 0) {
        FILE *dst = fopen(hdr.name, "wb");

        if (dst) {
            char block[BLOCK_SIZE];
            size_t total = 0;

            while (total < hdr.size) {
                size_t left_over = hdr.size - total;
                size_t to_read = left_over >= BLOCK_SIZE ? BLOCK_SIZE : left_over;
                size_t n = fread(block, 1, to_read, archive);

                total += n;
                fwrite(block, 1, n, dst);
            }

            fclose(dst);
        }
    }
}

static char *generate_archive_name(void)
{
    static char archive_name[MAX_NAME];
    
    char salt[5];
    time_t now = time(NULL);
    struct tm *curr = localtime(&now);

    strftime(archive_name, sizeof archive_name, "%Y%m%d%H%M%S", curr);
    sprintf(salt, "%d", rand() % 10000);
    strcat(archive_name, salt);
    strcat(archive_name, ".ar");

    return archive_name;
}

int main(int argc, char *argv[])
{
    srand((unsigned)time(NULL));

    if (argc > 1) {
        if (strcmp(argv[1], ARCHIVE) == 0) {
            FILE *archive = fopen(generate_archive_name(), "ab");

            if (archive) {
                int i;

                for (i = 2; i < argc; i++) {
                    FILE *in = fopen(argv[i], "rb");

                    if (in) {
                        add_to_archive(archive, in, argv[i]);
                        fclose(in);
                    }
                }

                fclose(archive);
            }
        }
        else if (strcmp(argv[1], EXTRACT) == 0) {
            if (argc > 2) {
                FILE *archive = fopen(argv[2], "rb");

                if (archive) {
                    extract_all(archive);
                    fclose(archive);
                }
            }
        }
        else {
            fprintf(stderr, "Invalid switch '%s'\n", argv[1]);
            return EXIT_FAILURE;
        }
    }

    return EXIT_SUCCESS;
}

Thank you so much for your time and effort and contribution for helping me.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.