From: Robert Pengelly Date: Thu, 26 Feb 2026 18:57:14 +0000 (+0000) Subject: Initial commit X-Git-Url: https://git.candlhat.org/?a=commitdiff_plain;ds=inline;p=tar.git Initial commit --- c0daad4aeb470fe2a24aa5d0d12f5ebb34aece4a diff --git a/LICENSE b/LICENSE new file mode 100755 index 0000000..fdddb29 --- /dev/null +++ b/LICENSE @@ -0,0 +1,24 @@ +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +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 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. + +For more information, please refer to diff --git a/Makefile.unix b/Makefile.unix new file mode 100755 index 0000000..5b21898 --- /dev/null +++ b/Makefile.unix @@ -0,0 +1,26 @@ +#****************************************************************************** +# @file Makefile.unix +#****************************************************************************** +SRCDIR ?= $(CURDIR) +VPATH := $(SRCDIR) + +CC := gcc +CFLAGS := -D_FILE_OFFSET_BITS=64 -O2 -Wall -Werror -Wextra -ansi -pedantic -std=c90 + +CSRC := lib.c tar.c report.c + +ifeq ($(OS), Windows_NT) +all: tar.exe + +tar.exe: $(CSRC) + $(CC) $(CFLAGS) -o $@ $^ +else +all: tar + +tar: $(CSRC) + $(CC) $(CFLAGS) -o $@ $^ +endif + +clean: + if [ -f tar.exe ]; then rm -rf tar.exe; fi + if [ -f tar ]; then rm -rf tar; fi diff --git a/Makefile.w32 b/Makefile.w32 new file mode 100755 index 0000000..50641d5 --- /dev/null +++ b/Makefile.w32 @@ -0,0 +1,19 @@ +#****************************************************************************** +# @file Makefile.w32 +#****************************************************************************** +SRCDIR ?= $(CURDIR) +VPATH := $(SRCDIR) + +CC := gcc +CFLAGS := -D_FILE_OFFSET_BITS=64 -O2 -Wall -Werror -Wextra + +CSRC := lib.c tar.c report.c + +all: tar.exe + +clean: + if exist tar.exe ( del /q tar.exe ) + if exist tar ( del /q tar ) + +tar.exe: $(CSRC) + $(CC) $(CFLAGS) -o $@ $^ diff --git a/README.md b/README.md new file mode 100755 index 0000000..c192a0a --- /dev/null +++ b/README.md @@ -0,0 +1,23 @@ +All source code is Public Domain. + +## Obtain the source code + + git clone https://git.candlhat.org/tar.git + +## Building + + BSD: + + Make sure you have gcc and gmake installed then run gmake -f Makefile.unix. + + Linux: + + Make sure you have gcc and make installed then run make -f Makefile.unix. + + macOS: + + Make sure you have xcode command line tools installed then run make -f Makefile.unix. + + Windows: + + Make sure you have mingw installed and the location within your PATH variable then run mingw32-make.exe -f Makefile.w32. diff --git a/lib.c b/lib.c new file mode 100755 index 0000000..0e13aa4 --- /dev/null +++ b/lib.c @@ -0,0 +1,340 @@ +/****************************************************************************** + * @file lib.c + *****************************************************************************/ +#include +#include +#include + +#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; + +} diff --git a/lib.h b/lib.h new file mode 100755 index 0000000..5f1506f --- /dev/null +++ b/lib.h @@ -0,0 +1,15 @@ +/****************************************************************************** + * @file lib.h + *****************************************************************************/ +#ifndef _LIB_H +#define _LIB_H + +char *xstrdup (const char *__str); + +void dynarray_add (void *ptab, long *nb_ptr, void *data); +void parse_args (int argc, char **argv, int optind); + +void *xmalloc (unsigned long __size); +void *xrealloc (void *__ptr, unsigned long __size); + +#endif /* _LIB_H */ diff --git a/report.c b/report.c new file mode 100755 index 0000000..8e128ef --- /dev/null +++ b/report.c @@ -0,0 +1,150 @@ +/****************************************************************************** + * @file report.c + *****************************************************************************/ +#include +#include +#include + +#include "report.h" + +unsigned long errors = 0; + +#ifndef __PDOS__ +#if defined (_WIN32) +# include +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); + +} diff --git a/report.h b/report.h new file mode 100755 index 0000000..8fc8758 --- /dev/null +++ b/report.h @@ -0,0 +1,29 @@ +/****************************************************************************** + * @file report.h + *****************************************************************************/ +#ifndef _REPORT_H +#define _REPORT_H + +enum report_type { + + REPORT_ERROR = 0, + REPORT_FATAL_ERROR, + REPORT_INTERNAL_ERROR, + REPORT_WARNING + +}; + +#if defined (_WIN32) +# define COLOR_ERROR 12 +# define COLOR_WARNING 13 +# define COLOR_INTERNAL_ERROR 19 +#else +# define COLOR_ERROR 91 +# define COLOR_INTERNAL_ERROR 94 +# define COLOR_WARNING 95 +#endif + +unsigned long get_error_count (void); +void report_at (const char *filename, unsigned long line_number, enum report_type type, const char *fmt, ...); + +#endif /* _REPORT_H */ diff --git a/tar.c b/tar.c new file mode 100755 index 0000000..8ec2cb3 --- /dev/null +++ b/tar.c @@ -0,0 +1,594 @@ +/****************************************************************************** + * @file tar.c + *****************************************************************************/ +#include +#include +#include +#include +#include + +#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 +# include +# include + +#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 + +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); + +} diff --git a/tar.h b/tar.h new file mode 100755 index 0000000..bf05a87 --- /dev/null +++ b/tar.h @@ -0,0 +1,20 @@ +/****************************************************************************** + * @file tar.h + *****************************************************************************/ +#ifndef _TAR_H +#define _TAR_H + +struct tar_state { + + char **paths; + long nb_paths; + + char *target, *root, *mode; + int append, create; + +}; + +extern struct tar_state *state; +extern const char *program_name; + +#endif /* _TAR_H */