--- /dev/null
+/******************************************************************************
+ * @file lib.c
+ *****************************************************************************/
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lib.h"
+#include "report.h"
+#include "tar.h"
+
+struct option {
+
+ const char *name;
+ int index, flags;
+
+};
+
+#define OPTION_NO_ARG 0x0001
+#define OPTION_HAS_ARG 0x0002
+
+enum options {
+
+ OPTION_IGNORED = 0,
+ OPTION_APPEND,
+ OPTION_CREATE,
+ OPTION_FILE,
+ OPTION_HELP,
+ OPTION_MODE,
+ OPTION_ROOT
+
+};
+
+static struct option opts[] = {
+
+ { "-C", OPTION_ROOT, OPTION_HAS_ARG },
+
+ { "-c", OPTION_CREATE, OPTION_NO_ARG },
+ { "-f", OPTION_FILE, OPTION_HAS_ARG },
+ { "-r", OPTION_APPEND, OPTION_NO_ARG },
+
+ { "--append", OPTION_APPEND, OPTION_NO_ARG },
+ { "--create", OPTION_CREATE, OPTION_NO_ARG },
+ { "--file", OPTION_FILE, OPTION_HAS_ARG },
+ { "--help", OPTION_HELP, OPTION_NO_ARG },
+ { "--mode", OPTION_MODE, OPTION_HAS_ARG },
+
+ { "--directory", OPTION_ROOT, OPTION_HAS_ARG },
+ { 0, 0, 0 }
+
+};
+
+static char *format_path (char *__str) {
+
+ unsigned long len, i;
+ char *p = __str;
+
+ len = strlen (p);
+
+ for (i = 0; i < len; i++) {
+
+ if (p[i] == '\\') {
+ p[i] = '/';
+ }
+
+ if (p[i] == '/') {
+
+ i++;
+
+ while (p[i] == '/' || p[i] == '\\') {
+ memmove (p + i, p + i + 1, strlen (p + i));
+ }
+
+ }
+
+ }
+
+ return xstrdup (p);
+
+}
+
+static int strstart (const char *val, const char **str) {
+
+ const char *p = val;
+ const char *q = *str;
+
+ while (*p != '\0') {
+
+ if (*p != *q) {
+ return 0;
+ }
+
+ ++p;
+ ++q;
+
+ }
+
+ *str = q;
+ return 1;
+
+}
+
+static void print_help (int exitval) {
+
+ if (!program_name) {
+ goto _exit;
+ }
+
+ fprintf (stderr, "Usage: %s [options] [file]...\n\n", program_name);
+ fprintf (stderr, "Options:\n\n");
+ fprintf (stderr, " -c, --create Create a new archive.\n");
+ /*fprintf (stderr, " -r, --append Append files to the end of the archive.\n");*/
+ fprintf (stderr, "\n");
+ fprintf (stderr, " -f ARCHIVE, --file ARCHIVE Use archive file or device ARCHIVE.\n");
+ fprintf (stderr, " --help Print this help information.\n");
+ fprintf (stderr, "\n");
+
+_exit:
+
+ exit (exitval);
+
+}
+
+char *xstrdup (const char *__str) {
+
+ char *ptr = xmalloc (strlen (__str) + 1);
+ strcpy (ptr, __str);
+
+ return ptr;
+
+}
+
+void dynarray_add (void *ptab, long *nb_ptr, void *data) {
+
+ int nb, nb_alloc;
+ void **pp;
+
+ nb = *nb_ptr;
+ pp = *(void ***) ptab;
+
+ if ((nb & (nb - 1)) == 0) {
+
+ if (!nb) {
+ nb_alloc = 1;
+ } else {
+ nb_alloc = nb * 2;
+ }
+
+ pp = xrealloc (pp, nb_alloc * sizeof (void *));
+ *(void ***) ptab = pp;
+
+ }
+
+ pp[nb++] = data;
+ *nb_ptr = nb;
+
+}
+
+void parse_args (int argc, char **argv, int optind) {
+
+ const char *optarg, *r;
+ struct option *popt;
+
+ if (optind >= argc) {
+ print_help (EXIT_FAILURE);
+ }
+
+ while (optind < argc) {
+
+ r = argv[optind++];
+
+ if (r[0] != '-' || r[1] == '\0') {
+
+ char *p = xstrdup (r);
+
+ dynarray_add (&state->paths, &state->nb_paths, format_path (p));
+ free (p);
+
+ continue;
+
+ }
+
+ for (popt = opts; popt; popt++) {
+
+ const char *p1 = popt->name;
+ const char *r1 = r;
+
+ if (!p1) {
+
+ report_at (program_name, 0, REPORT_ERROR, "invalid option -- '%s'", r);
+ exit (EXIT_FAILURE);
+
+ }
+
+ if (!strstart (p1, &r1)) {
+ continue;
+ }
+
+ optarg = r1;
+
+ if (popt->flags & OPTION_HAS_ARG) {
+
+ if (*optarg == '\0') {
+
+ if (optind >= argc) {
+
+ report_at (program_name, 0, REPORT_ERROR, "argument to '%s' is missing", r);
+ exit (EXIT_FAILURE);
+
+ }
+
+ optarg = argv[optind++];
+
+ }
+
+ } else if (*optarg != '\0') {
+ continue;
+ }
+
+ break;
+
+ }
+
+ switch (popt->index) {
+
+ case OPTION_APPEND: {
+
+ state->append = 1;
+ break;
+
+ }
+
+ case OPTION_CREATE: {
+
+ state->create = 1;
+ break;
+
+ }
+
+ case OPTION_FILE: {
+
+ if (state->target) {
+
+ report_at (program_name, 0, REPORT_ERROR, "multiple archive files provided");
+ exit (EXIT_FAILURE);
+
+ }
+
+ state->target = xstrdup (optarg);
+ break;
+
+ }
+
+ case OPTION_HELP: {
+
+ print_help (EXIT_SUCCESS);
+ break;
+
+ }
+
+ case OPTION_MODE: {
+
+ if (strlen (optarg) != 3 && strlen (optarg) != 4) {
+
+ report_at (program_name, 0, REPORT_ERROR, "invalid mode provided");
+ exit (EXIT_FAILURE);
+
+ }
+
+ if (state->mode) { free (state->mode); }
+ state->mode = xmalloc (5);
+
+ if (strlen (optarg) == 3) {
+
+ state->mode[0] = '0';
+ memcpy (state->mode + 1, optarg, 3);
+
+ } else {
+ memcpy (state->mode, optarg, 4);
+ }
+
+ break;
+
+ }
+
+ case OPTION_ROOT: {
+
+ char *p = xstrdup (optarg);
+
+ if (state->root) { free (state->root); }
+ state->root = format_path (p);
+
+ free (p);
+ break;
+
+ }
+
+ default: {
+
+ report_at (program_name, 0, REPORT_ERROR, "unsupported option '%s'", r);
+ exit (EXIT_FAILURE);
+
+ }
+
+ }
+
+ }
+
+}
+
+void *xmalloc (unsigned long __size) {
+
+ void *p = malloc (__size);
+
+ if (!p && __size) {
+
+ report_at (program_name, 0, REPORT_ERROR, "failed to allocate %ld bytes of memory", __size);
+ exit (EXIT_FAILURE);
+
+ }
+
+ memset (p, 0, __size);
+ return p;
+
+}
+
+void *xrealloc (void *__ptr, unsigned long __size) {
+
+ void *p = realloc (__ptr, __size);
+
+ if (!p && __size) {
+
+ report_at (program_name, 0, REPORT_ERROR, "failed to allocate %ld bytes of memory", __size);
+ exit (EXIT_FAILURE);
+
+ }
+
+ return p;
+
+}
--- /dev/null
+/******************************************************************************
+ * @file report.c
+ *****************************************************************************/
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "report.h"
+
+unsigned long errors = 0;
+
+#ifndef __PDOS__
+#if defined (_WIN32)
+# include <windows.h>
+static int OriginalConsoleColor = -1;
+#endif
+
+static void reset_console_color (void) {
+
+#if defined (_WIN32)
+
+ HANDLE hStdError = GetStdHandle (STD_ERROR_HANDLE);
+
+ if (OriginalConsoleColor == -1) { return; }
+
+ SetConsoleTextAttribute (hStdError, OriginalConsoleColor);
+ OriginalConsoleColor = -1;
+
+#else
+
+ fprintf (stderr, "\033[0m");
+
+#endif
+
+}
+
+static void set_console_color (int color) {
+
+#if defined (_WIN32)
+
+ HANDLE hStdError = GetStdHandle (STD_ERROR_HANDLE);
+ WORD wColor;
+
+ if (OriginalConsoleColor == -1) {
+
+ CONSOLE_SCREEN_BUFFER_INFO csbi;
+
+ if (!GetConsoleScreenBufferInfo (hStdError, &csbi)) {
+ return;
+ }
+
+ OriginalConsoleColor = csbi.wAttributes;
+
+ }
+
+ wColor = (OriginalConsoleColor & 0xF0) + (color & 0xF);
+ SetConsoleTextAttribute (hStdError, wColor);
+
+#else
+
+ fprintf (stderr, "\033[%dm", color);
+
+#endif
+
+}
+#endif
+
+static void output_message (const char *filename, unsigned long lineno, unsigned long idx, enum report_type type, const char *fmt, va_list ap) {
+
+ if (filename) {
+
+ if (lineno == 0 && idx == 0) {
+ fprintf (stderr, "%s: ", filename);
+ } else {
+ fprintf (stderr, "%s:", filename);
+ }
+
+ }
+
+ if (lineno > 0) {
+
+ if (idx == 0) {
+ fprintf (stderr, "%lu: ", lineno);
+ } else {
+ fprintf (stderr, "%lu:", lineno);
+ }
+
+ }
+
+ if (idx > 0) {
+ fprintf (stderr, "%lu: ", idx);
+ }
+
+ if (type == REPORT_ERROR || type == REPORT_FATAL_ERROR) {
+
+#ifndef __PDOS__
+ set_console_color (COLOR_ERROR);
+#endif
+
+ if (type == REPORT_ERROR) {
+ fprintf (stderr, "error:");
+ } else {
+ fprintf (stderr, "fatal error:");
+ }
+
+ } else if (type == REPORT_INTERNAL_ERROR) {
+
+#ifndef __PDOS__
+ set_console_color (COLOR_INTERNAL_ERROR);
+#endif
+
+ fprintf (stderr, "internal error:");
+
+ } else if (type == REPORT_WARNING) {
+
+#ifndef __PDOS__
+ set_console_color (COLOR_WARNING);
+#endif
+
+ fprintf (stderr, "warning:");
+
+ }
+
+#ifndef __PDOS__
+ reset_console_color ();
+#endif
+
+ fprintf (stderr, " ");
+ vfprintf (stderr, fmt, ap);
+ fprintf (stderr, "\n");
+
+ if (type != REPORT_WARNING) {
+ ++errors;
+ }
+
+}
+
+unsigned long get_error_count (void) {
+ return errors;
+}
+
+void report_at (const char *filename, unsigned long lineno, enum report_type type, const char *fmt, ...) {
+
+ va_list ap;
+
+ va_start (ap, fmt);
+ output_message (filename, lineno, 0, type, fmt, ap);
+ va_end (ap);
+
+}
--- /dev/null
+/******************************************************************************
+ * @file tar.c
+ *****************************************************************************/
+#include <limits.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lib.h"
+#include "report.h"
+#include "tar.h"
+
+struct tar_state *state = 0;
+const char *program_name = 0;
+
+struct tar_raw_header {
+
+ char name[100];
+ char mode[8];
+ char owner[8];
+ char group[8];
+ char size[12];
+ char mtime[12];
+ char checksum[8];
+ char type;
+ char linkname[100];
+ char _padding[255];
+
+};
+
+static unsigned int checksum (struct tar_raw_header *header) {
+
+ unsigned char *p = (unsigned char *) header;
+ unsigned int res = 256, i;
+
+ for (i = 0; i < offsetof (struct tar_raw_header, checksum); i++) {
+ res += p[i];
+ }
+
+ for (i = offsetof (struct tar_raw_header, type); i < sizeof (*header); i++) {
+ res += p[i];
+ }
+
+ return res;
+
+}
+
+static int write_null_bytes (FILE *fp, int n) {
+
+ char nul = '\0';
+ int i;
+
+ for (i = 0; i < n; i++) {
+
+ if (fwrite (&nul, 1, 1, fp) != 1) {
+ return 1;
+ }
+
+ }
+
+ return 0;
+
+}
+
+#if defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))
+# include <sys/stat.h>
+# include <dirent.h>
+# include <unistd.h>
+
+#ifndef S_IFDIR
+# define S_IFDIR 0040000
+#endif
+
+#ifndef S_IFREG
+# define S_IFREG 0100000
+#endif
+
+static void add_entry (FILE *tarfp, const char *root, const char *path) {
+
+ struct tar_raw_header header;
+ struct stat stbuf;
+
+ const char *p = path;
+ char *p2;
+
+ unsigned long len;
+ int mode;
+
+ while (*p == '/') {
+ p++;
+ }
+
+ if (root) {
+
+ len = strlen (root);
+
+ if (root[len - 1] == '/') {
+
+ len += (strlen (p) + 1);
+
+ p2 = xmalloc (len);
+ sprintf (p2, "%s%s", root, p);
+
+ } else {
+
+ len += (1 + strlen (p) + 1);
+
+ p2 = xmalloc (len);
+ sprintf (p2, "%s/%s", root, p);
+
+ }
+
+ } else {
+ p2 = xstrdup (path);
+ }
+
+ if (stat (p2, &stbuf) < 0) {
+
+ report_at (program_name, 0, REPORT_ERROR, "failed to stat '%s'", p);
+ return;
+
+ }
+
+ if (state->mode) {
+
+ int o1, o2, o3;
+
+ o1 = state->mode[1] - '0';
+ o2 = state->mode[2] - '0';
+ o3 = state->mode[3] - '0';
+
+ mode = (o1 << 6) | (o2 << 3) | o3;
+
+ } else {
+ mode = stbuf.st_mode & 07777;
+ }
+
+ memset (&header, 0, sizeof (header));
+ strcpy (header.name, p);
+
+ sprintf (header.mode, "%o", mode);
+ sprintf (header.owner, "%o", 0);
+ sprintf (header.mtime, "%o", 0);
+
+ if (stbuf.st_mode & S_IFDIR) {
+
+ DIR *dirp;
+
+ struct dirent *de;
+ char *subpath;
+
+ sprintf (header.size, "%o", 0);
+ header.type = '5'; /* MTAR_TDIR */
+
+ sprintf (header.checksum, "%06o", checksum (&header));
+ header.checksum[7] = ' ';
+
+ if (fwrite (&header, 1, sizeof (header), tarfp) != sizeof (header)) {
+ report_at (program_name, 0, REPORT_ERROR, "failed to write header to '%s'", state->target);
+ }
+
+ if (!(dirp = opendir (p2))) {
+
+ report_at (program_name, 0, REPORT_ERROR, "failed to open directory '%s'", p);
+ return;
+
+ }
+
+ while ((de = readdir (dirp))) {
+
+ if (de->d_name[0] == '.' && (de->d_name[1] == '\0' || (de->d_name[1] == '.' && de->d_name[2] == '\0'))) {
+ continue;
+ }
+
+ len = strlen (path);
+
+ if (path[len - 1] == '/') {
+
+ len += (strlen (de->d_name) + 1);
+
+ if (!(subpath = malloc (len))) {
+
+ report_at (program_name, 0, REPORT_ERROR, "failed to allocate memory for '%s'", de->d_name);
+ continue;
+
+ }
+
+ sprintf (subpath, "%s%s", path, de->d_name);
+
+ } else {
+
+ len += (1 + strlen (de->d_name) + 1);
+
+ if (!(subpath = malloc (len))) {
+
+ report_at (program_name, 0, REPORT_ERROR, "failed to allocate memory for '%s'", de->d_name);
+ continue;
+
+ }
+
+ sprintf (subpath, "%s/%s", path, de->d_name);
+
+ }
+
+ add_entry (tarfp, root, subpath);
+ free (subpath);
+
+ }
+
+ closedir (dirp);
+
+ } else if (stbuf.st_mode & S_IFREG) {
+
+ FILE *fp;
+
+ unsigned char *data;
+ unsigned long bytes, read;
+
+ if (!(fp = fopen (p2, "r+b"))) {
+
+ report_at (program_name, 0, REPORT_ERROR, "failed to open '%s' for reading", path);
+ goto _end;
+
+ }
+
+ fseek (fp, 0, SEEK_END);
+ bytes = ftell (fp);
+
+ fseek (fp, 0, SEEK_SET);
+
+ sprintf (header.size, "%lo", bytes);
+ header.type = '0'; /* MTAR_TREG */
+
+ sprintf (header.checksum, "%06o", checksum (&header));
+ header.checksum[7] = ' ';
+
+ if (fwrite (&header, 1, sizeof (header), tarfp) != sizeof (header)) {
+
+ report_at (program_name, 0, REPORT_ERROR, "failed to write header to '%s'", state->target);
+ fclose (fp);
+
+ goto _end;
+
+ }
+
+ data = xmalloc (512);
+
+ for (;;) {
+
+ if (bytes == 0 || feof (fp)) {
+ break;
+ }
+
+ read = (bytes > 512 ? 512 : bytes);
+
+ if (fread (data, 1, read, fp) != read) {
+
+ report_at (program_name, 0, REPORT_ERROR, "failed to read %lu bytes from %s", read, path);
+ break;
+
+ }
+
+ if (fwrite (data, 1, read, tarfp) != read) {
+
+ report_at (program_name, 0, REPORT_ERROR, "failed to write %lu bytes to %s", read, state->target);
+ break;
+
+ }
+
+ bytes -= read;
+
+ }
+
+ bytes = (ftell (fp) % 512);
+ fclose (fp);
+
+ if (bytes != 0) {
+ write_null_bytes (tarfp, 512 - bytes);
+ }
+
+ }
+
+_end:
+
+ free (p2);
+
+}
+#elif defined (_WIN32)
+# include <windows.h>
+
+static BOOL IsDirectory (LPCTSTR szPath) {
+
+ DWORD dwAttrib = GetFileAttributes (szPath);
+ return (dwAttrib != INVALID_FILE_ATTRIBUTES && (dwAttrib & FILE_ATTRIBUTE_DIRECTORY));
+
+}
+
+static void add_entry (FILE *tarfp, const char *root, const char *path) {
+
+ struct tar_raw_header header;
+
+ const char *p = path;
+ char *p2;
+
+ unsigned long len;
+ int mode;
+
+ if (isalpha ((int) p[0]) && p[1] == ':') {
+ p += 2;
+ }
+
+ while (*p == '/') {
+ p++;
+ }
+
+ if (root) {
+
+ len = strlen (root);
+
+ if (root[len - 1] == '/') {
+
+ len += (strlen (p) + 1);
+
+ p2 = xmalloc (len);
+ sprintf (p2, "%s%s", root, p);
+
+ } else {
+
+ len += (1 + strlen (p) + 1);
+
+ p2 = xmalloc (len);
+ sprintf (p2, "%s/%s", root, p);
+
+ }
+
+ } else {
+ p2 = xstrdup (path);
+ }
+
+ memset (&header, 0, sizeof (header));
+ strcpy (header.name, p);
+
+ sprintf (header.owner, "%o", 0);
+ sprintf (header.mtime, "%o", 0);
+
+ if (state->mode) {
+
+ int o1, o2, o3;
+
+ o1 = state->mode[1] - '0';
+ o2 = state->mode[2] - '0';
+ o3 = state->mode[3] - '0';
+
+ mode = (o1 << 6) | (o2 << 3) | o3;
+
+ } else {
+ mode = (IsDirectory (p2) ? 775 : 644);
+ }
+
+ sprintf (header.mode, "%o", mode);
+
+ if (IsDirectory (p2)) {
+
+ char *subpath, *filename;
+
+ HANDLE hFind;
+ WIN32_FIND_DATA FindFileData;
+
+ sprintf (header.size, "%o", 0);
+ header.type = '5'; /* MTAR_TDIR */
+
+ sprintf (header.checksum, "%06o", checksum (&header));
+ header.checksum[7] = ' ';
+
+ if (fwrite (&header, 1, sizeof (header), tarfp) != sizeof (header)) {
+ report_at (program_name, 0, REPORT_ERROR, "failed to write header to '%s'", state->target);
+ }
+
+ len = strlen (p2);
+
+ if (p2[len - 1] == '/') {
+
+ subpath = xmalloc (len + 5);
+ sprintf (subpath, "%s*.*", p2);
+
+ } else {
+
+ subpath = xmalloc (len + 6);
+ sprintf (subpath, "%s/*.*", p2);
+
+ }
+
+ if ((hFind = FindFirstFile (subpath, &FindFileData)) == INVALID_HANDLE_VALUE) {
+
+ free (subpath);
+ return;
+
+ }
+
+ free (subpath);
+
+ do {
+
+ filename = FindFileData.cFileName;
+
+ if (filename[0] == '.' && (filename[1] == '\0' || (filename[1] == '.' && filename[2] == '\0'))) {
+ continue;
+ }
+
+ len = strlen (path);
+
+ if (path[len - 1] == '/') {
+
+ len += (strlen (filename) + 1);
+
+ if (!(subpath = malloc (len))) {
+
+ report_at (program_name, 0, REPORT_ERROR, "failed to allocate memory for '%s'", filename);
+ continue;
+
+ }
+
+ sprintf (subpath, "%s%s", path, filename);
+
+ } else {
+
+ len += (1 + strlen (filename) + 1);
+
+ if (!(subpath = malloc (len))) {
+
+ report_at (program_name, 0, REPORT_ERROR, "failed to allocate memory for '%s'", filename);
+ continue;
+
+ }
+
+ sprintf (subpath, "%s/%s", path, filename);
+
+ }
+
+ add_entry (tarfp, root, subpath);
+ free (subpath);
+
+ } while (FindNextFile (hFind, &FindFileData) != 0);
+
+ FindClose (hFind);
+
+ } else {
+
+ FILE *fp;
+
+ unsigned char *data;
+ unsigned long bytes, read;
+
+ if (!(fp = fopen (p2, "r+b"))) {
+
+ report_at (program_name, 0, REPORT_ERROR, "failed to open '%s' for reading", path);
+ goto _end;
+
+ }
+
+ fseek (fp, 0, SEEK_END);
+ bytes = ftell (fp);
+
+ fseek (fp, 0, SEEK_SET);
+
+ sprintf (header.size, "%lo", bytes);
+ header.type = '0'; /* MTAR_TREG */
+
+ sprintf (header.checksum, "%06o", checksum (&header));
+ header.checksum[7] = ' ';
+
+ if (fwrite (&header, 1, sizeof (header), tarfp) != sizeof (header)) {
+
+ report_at (program_name, 0, REPORT_ERROR, "failed to write header to '%s'", state->target);
+ fclose (fp);
+
+ goto _end;
+
+ }
+
+ data = xmalloc (512);
+
+ for (;;) {
+
+ if (bytes == 0 || feof (fp)) {
+ break;
+ }
+
+ read = (bytes > 512 ? 512 : bytes);
+
+ if (fread (data, 1, read, fp) != read) {
+
+ report_at (program_name, 0, REPORT_ERROR, "failed to read %lu bytes from %s", read, path);
+ break;
+
+ }
+
+ if (fwrite (data, 1, read, tarfp) != read) {
+
+ report_at (program_name, 0, REPORT_ERROR, "failed to write %lu bytes to %s", read, state->target);
+ break;
+
+ }
+
+ bytes -= read;
+
+ }
+
+ bytes = ftell (fp) % 512;
+ fclose (fp);
+
+ if (bytes != 0) {
+ write_null_bytes (tarfp, 512 - bytes);
+ }
+
+ }
+
+_end:
+
+ free (p2);
+
+}
+#endif
+
+
+int main (int argc, char **argv) {
+
+ FILE *tarfp;
+ long i;
+
+ char *mode;
+ int action;
+
+ if (argc && *argv) {
+
+ const char *p;
+ program_name = *argv;
+
+ if ((p = strrchr (program_name, '/')) || (p = strrchr (program_name, '\\'))) {
+ program_name = (p + 1);
+ }
+
+ }
+
+ state = xmalloc (sizeof (*state));
+ parse_args (argc, argv, 1);
+
+ if (!state->target) {
+
+ report_at (program_name, 0, REPORT_ERROR, "missing -f");
+ return EXIT_FAILURE;
+
+ }
+
+ action = (state->create + state->append);
+
+ if (action != 1) {
+
+ if (action < 1) {
+ report_at (program_name, 0, REPORT_ERROR, "you must specify one of the '-cr' options");
+ } else {
+ report_at (program_name, 0, REPORT_ERROR, "you may not specify more than one '-cr' option");
+ }
+
+ return EXIT_FAILURE;
+
+ }
+
+ if (state->create) {
+ mode = "w+b";
+ } else {
+ mode = "r+b";
+ }
+
+ if (!(tarfp = fopen (state->target, mode))) {
+
+ report_at (program_name, 0, REPORT_ERROR, "failed to open '%s'", state->target);
+ return EXIT_FAILURE;
+
+ }
+
+ if (state->append) { fseek (tarfp, 0, SEEK_END); }
+
+ for (i = 0; i < state->nb_paths; i++) {
+ add_entry (tarfp, state->root, state->paths[i]);
+ }
+
+ write_null_bytes (tarfp, sizeof (struct tar_raw_header) * 2);
+
+ fclose (tarfp);
+ return (get_error_count () > 0 ? EXIT_FAILURE : EXIT_SUCCESS);
+
+}