diff options
author | Aidan MacDonald <amachronic@protonmail.com> | 2021-11-04 22:57:17 +0300 |
---|---|---|
committer | Aidan MacDonald <amachronic@protonmail.com> | 2021-11-05 03:24:35 +0300 |
commit | e53e099d41eb0b069300de80a22cc9cda579df82 (patch) | |
tree | 917c33235b2051f61966e633b74bc19ab8337b98 | |
parent | 14b9bf948fc6fb591d3b1db104ff546612377fb5 (diff) |
Add Makefile and demo app
The demo app can list contents and extract tar files using
microtar, and serves to demonstrate the API.
-rw-r--r-- | .gitignore | 3 | ||||
-rw-r--r-- | Makefile | 21 | ||||
-rw-r--r-- | mtar.c | 191 |
3 files changed, 215 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..cccd994 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*.o +*.a +/mtar diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..b2142ec --- /dev/null +++ b/Makefile @@ -0,0 +1,21 @@ +CPPFLAGS = -Isrc +CFLAGS = -std=c99 -Wall -Wextra + +MTAR_OBJ = mtar.o +MTAR_BIN = mtar + +MICROTAR_OBJ = src/microtar.o src/microtar-stdio.o +MICROTAR_LIB = libmicrotar.a + +$(MTAR_BIN): $(MTAR_OBJ) $(MICROTAR_LIB) + $(CC) $(CFLAGS) $(CPPFLAGS) -o $@ $^ + +$(MICROTAR_LIB): $(MICROTAR_OBJ) + $(AR) r $@ $^ + +%.o: %.c + $(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $@ + +clean: + rm -f $(MICROTAR_LIB) $(MICROTAR_OBJ) + rm -f $(MTAR_BIN) $(MTAR_OBJ) @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2021 Aidan MacDonald + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "microtar.h" +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <stdarg.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <unistd.h> + +/* exit codes */ +#define E_TAR 1 +#define E_FS 2 +#define E_OTHER 4 +#define E_ARGS 8 + +enum { + OP_LIST, + OP_CREATE, + OP_EXTRACT, +}; + +void die(int err, const char* msg, ...) +{ + fprintf(stderr, "mtar: "); + + va_list ap; + va_start(ap, msg); + vfprintf(stderr, msg, ap); + va_end(ap); + + fprintf(stderr, "\n"); + exit(err); +} + +int list_foreach_cb(mtar_t* tar, const mtar_header_t* h, void* arg) +{ + (void)tar; + (void)arg; + printf("%s\n", h->name); + return 0; +} + +void list_files(mtar_t* tar) +{ + int err = mtar_foreach(tar, list_foreach_cb, NULL); + if(err) + die(E_TAR, "listing failed: %s", mtar_strerror(err)); +} + +struct extract_args { + char** names; + int count; +}; + +int extract_foreach_cb(mtar_t* tar, const mtar_header_t* h, void* arg) +{ + struct extract_args* args = arg; + (void)args; /* TODO */ + + if(h->type == MTAR_TDIR) { + if(mkdir(h->name, h->mode) != 0) + die(E_FS, "cannot create directory \"%s\"", h->name); + return 0; + } + + if(h->type != MTAR_TREG) { + fprintf(stderr, "warning: not extracting unsupported type \"%s\"", h->name); + return 0; + } + + int fd = open(h->name, O_CREAT|O_WRONLY, h->mode); + if(fd < 0) + die(E_FS, "extracting \"%s\" failed: %s", h->name, strerror(errno)); + + char iobuf[1024]; + while(!mtar_eof_data(tar)) { + int rcount = mtar_read_data(tar, iobuf, sizeof(iobuf)); + if(rcount < 0) + die(E_TAR, "extracting \"%s\" failed: %s", h->name, mtar_strerror(rcount)); + + int wcount = write(fd, iobuf, rcount); + if(wcount != rcount) + die(E_FS, "extracting \"%s\" failed: %s", h->name, strerror(errno)); + } + + close(fd); + return 0; +} + +void extract_files(mtar_t* tar, char** files, int num_files) +{ + struct extract_args args; + args.names = files; + args.count = num_files; + + int err = mtar_foreach(tar, extract_foreach_cb, &args); + if(err) + die(E_TAR, "extraction failed: %s", mtar_strerror(err)); +} + +int main(int argc, char* argv[]) +{ + ++argv, --argc; + if(argc == 0) + die(E_ARGS, "no input files"); + + if(!strcmp(*argv, "--help")) { + printf( +"usage:\n" +" mtar list tar-file\n" +" List the members of the given tar archive, one filename per line.\n" +"\n" +" mtar create tar-file members...\n" +" mtar add tar-file members...\n" +" Create a new tar archive from the files listed on the command line.\n" +" WARNING: Any existing file at tar-file will be overwritten!\n" +"\n" +" mtar extract tar-file [members...]\n" +" Extract the contents of the tar archive to the current directory.\n" +" If filenames are given, only the named members will be extracted.\n" +"\n"); + exit(E_ARGS); + } + + int op; + if(!strcmp(*argv, "list")) + op = OP_LIST; + else if(!strcmp(*argv, "create")) + op = OP_CREATE; + else if(!strcmp(*argv, "extract")) + op = OP_EXTRACT; + else + die(E_ARGS, "invalid operation \"%s\"", *argv); + ++argv, --argc; + + if(argc == 0) + die(E_ARGS, "missing archive name"); + const char* archive_name = *argv; + ++argv, --argc; + + if(op == OP_LIST && argc != 0) + die(E_ARGS, "excess arguments on command line"); + + mtar_t tar; + int err = mtar_open(&tar, archive_name, "rb"); + if(err) + die(E_TAR, "can't open archive: %s", mtar_strerror(err)); + + switch(op) { + case OP_LIST: + list_files(&tar); + break; + + case OP_EXTRACT: + extract_files(&tar, argv, argc); + break; + + default: + die(E_OTHER, "not implemented"); + break; + } + + err = mtar_close(&tar); + if(err) + die(E_TAR, "failed to finalize archive: %s", mtar_strerror(err)); + + return 0; +} |