Hi, I hope someone can help me with a problem that I am currently stuck on, basically I am trying to create a file archive utility which can archive 1 or more file each specified in a command line into a file.

My problem which I am currently stuck on is that, i have managed to create a file archive utility which can only archive 1 file.

e.g: My program can copy the contents of 1 file and put the contents of that file into a new file so, it can copy a file and its contents called hello and put that in a new file which can be named hello1, whatever the user wants to name the file.

I was also hoping if anyone can give me a clue on how to extract all of the files contained in the archive (specified on the command line) in to the current working
directory.

My code so far is:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>

main (int argc, char **argv)
{
    int bytes_read, bytes_written;
    struct stat inode;
    int input_fd, output_fd;
    char buffer[64];
    int eof = 0;
    int i;

    /* Check the command line arguments */
    if (argc != 3) 
    {
        printf("syntax is:         %s <fromfile> <tofile>\n", argv[0]);
        exit (1);
    }

    /* Check the input file exists and is a file */
    if ((stat(argv[1], &inode) == -1) || (!S_ISREG(inode.st_mode)))
    {
        printf("%s is not a file\n", argv[1]);
        exit(2);
    }

    /* Check that the output file doesnt exist */
    if (stat(argv[2], &inode) != -1)
    {
        printf("Warning: The file %s already exists. Not going to overwrite\n", argv[2]);
        exit(2);
    }

    /* Open the input file for reading */
    input_fd = open(argv[1], O_RDONLY, 0);
    if (input_fd == -1)
    {
        printf("%s cannot be opened\n", argv[1]);
        exit(3);
    }

    output_fd = open(argv[2], O_CREAT | O_WRONLY | O_EXCL , S_IRUSR|S_IWUSR);
    if (output_fd == -1)
    {
        printf("%s cannot be opened\n", argv[2]);
        exit(3);
    }

    /* Begin processing the input file here */
    while (!eof)
    {
        bytes_read = read(input_fd, buffer, sizeof(buffer));

        if (bytes_read == -1)
        {
            printf("%s cannot be read\n", argv[1]);
            exit(4);
        }

        if (bytes_read > 0)
        {
            bytes_written = write(output_fd, buffer, bytes_read);
            if (bytes_written == -1)
            {
                printf("There was an error writing to the file %s\n",argv[2]);
                exit(4);
            }

            if (bytes_written != bytes_read)
            {
                printf("Devistating failure! Bytes have either magically appeared and been written or dissapeard and been skipped.  Data is inconsistant!\n");
                exit(101);
            }
        } 
        else
        {
            eof = 1;
        }
    }
    close(input_fd);
    close(output_fd);

}

I'm hoping someone can help me with this.

Thanks a lot.

Recommended Answers

All 13 Replies

The archive file will have to contain some sort of header record for each of the files it contains so that the files can be reconstructed after being archived. The header record would probably contain the file name and file size. Most archive programs compress the files too so that the archive file is a lot smaller.

Is it possible to have a example of some sort please?
thanks

Not from me because I've never written an archive program and don't intend to do so now either. Shouldn't be too hard though, just use a structure for the header and save the structure at the beginning of each file.

struct header
{
  char filename[255]; // max possible file name
  unsigned long filesize; // size of the file
};

Look through the recent threads. Not only have I explained a simple way of writing an archiver several times in the last two weeks, I've also posted sample code.

Thanks, for the reply - Ancient Dragon, I'll try do something.
Narue - thanks for the reply, I looked through the forum earlier before making the post, is it possible for you to post the link to the thread please would be really helpful thanks.

This one is what I was thinking of. I even had a full blown application written to give as a sample, but it seems I deleted it.

Thanks a lot for the link and reply.
Is their no backup of the application you wrote, that would be really really helpful thanks a lot!

Is their no backup of the application you wrote, that would be really really helpful thanks a lot!

It's not in my usual scratch code locations, but if I find it I'll post it for you.

Thanks :)

I couldn't find it, but in a fit of boredom I wrote another:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/stat.h>

struct header {
    long size;
    char name[FILENAME_MAX + 1];
};

struct node {
    struct header hdr;
    struct node *next;
};

static long file_size(const char *path)
{
    struct stat file_stat;

    if (stat(path, &file_stat) != 0)
        return 0;

    return file_stat.st_size;
}

static const char *extract_filename(const char *path)
{
    const char *p = path + strlen(path);

    while (p != path && p[-1] != '\\' && p[-1] != '/')
        --p;

    return p;
}

static int archive_files(const char *archive_path, char **files)
{
    struct node *head = NULL;
    struct node *it;
    int files_archived = 0;

    /* Prepare the file list */
    for (; *files != NULL; ++files) {
        struct node *file = malloc(sizeof *file);

        if (file == NULL) {
            /* Out of memory is a fatal error */
            fputs("Out of memory\n", stderr);
            goto cleanup;
        }

        file->next = head;
        head = file;

        head->hdr.size = file_size(*files);

        /* Use failure in obtaining file size to check the path's validity */
        if (head->hdr.size == 0 || strlen(*files) > FILENAME_MAX) {
            fprintf(stderr, "Skipping '%s' due to path error\n", *files);
            continue;
        }

        strcpy(head->hdr.name, *files);
    }

    /* Perform archival on the file list */
    if (head != NULL) {
        FILE *archive = fopen(archive_path, "wb");

        if (archive == NULL) {
            /* Failure to open the archive file is a fatal error */
            perror("Error opening archive file");
            goto cleanup;
        }

        for (it = head; it != NULL; it = it->next) {
            FILE *source = fopen(it->hdr.name, "rb");
            long byte_count = 0;
            int byte;

            if (source == NULL) {
                fprintf(stderr, "Skipping '%s' due to file open error: %s\n", it->hdr.name, strerror(errno));
                continue;
            }

            if (fwrite(&it->hdr, sizeof it->hdr, 1, archive) != 1) {
                fprintf(stderr, "Skipping '%s' due to write error: %s\n", it->hdr.name, strerror(errno));
                continue;
            }

            while ((byte = getc(source)) != EOF) {
                if (++byte_count > it->hdr.size) {
                    fprintf(stderr, "File size mismatch detected in '%s'\n", it->hdr.name);
                    break;
                }

                if (putc(byte, archive) == EOF) {
                    fprintf(stderr, "Write error detected during archival: %s\n", strerror(errno));
                    break;
                }
            }

            if (byte != EOF)
                fprintf(stderr, "'%s' partially copied due to errors\n", it->hdr.name);

            ++files_archived;
            fclose(source);
        }

        fclose(archive);
    }

cleanup:
    while (head != NULL) {
        it = head->next;
        free(head);
        head = it;
    }

    return files_archived;
}

static int extract_files(const char *archive_path)
{
    FILE *archive = fopen(archive_path, "rb");
    int files_extracted = 0;

    if (archive != NULL) {
        struct header hdr;

        while (fread(&hdr, sizeof hdr, 1, archive) == 1) {
            char file[FILENAME_MAX + 1];
            long byte_count = 0;
            FILE *destination;
            int byte;

            /* Write to the current working directory */
            strcpy(file, extract_filename(hdr.name));
            destination = fopen(file, "wb");

            if (destination == NULL) {
                /* Failure to open an extracted file is a fatal error */
                perror("Error extracting file");
                break;
            }

            while ((byte = getc(archive)) != EOF) {
                if (++byte_count > hdr.size) {
                    ungetc(byte, archive);
                    break;
                }

                if (putc(byte, destination) == EOF) {
                    fprintf(stderr, "Write error detected during extraction: %s\n", strerror(errno));
                    break;
                }
            }

            ++files_extracted;
            fclose(destination);
        }

        fclose(archive);
    }

    return files_extracted;
}

int main(int argc, char *argv[])
{
    int rc = EXIT_SUCCESS;

    if (argc > 2) {
        if (strcmp(argv[1], "-a") == 0) {
            if (archive_files(argv[2], &argv[3]) == 0)
                rc = EXIT_FAILURE;
        }
        else if (strcmp(argv[1], "-e") == 0) {
            if (extract_files(argv[2]) == 0)
                rc = EXIT_FAILURE;
        }
    }

    return rc;
}

In before "oh noes, you used goto!". :icon_rolleyes:

the program can be ammended, using fflush instead of goto?

the program can be ammended, using fflush instead of goto?

Um, no. fflush does something completely different.

How to run this program sir? This is something new so was wondering what commands to type in to archive or extract files or folder in the terminal? Thank you.

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.