From d7ae5ccfda2892d7804ab86039a740547c089158 Mon Sep 17 00:00:00 2001 From: Robert Pengelly Date: Mon, 3 Jun 2024 04:34:19 +0100 Subject: [PATCH] New server --- LICENSE | 24 +++ Makefile.p32 | 25 +++ Makefile.pdw | 25 +++ Makefile.unix | 28 +++ Makefile.w32 | 21 +++ README.md | 32 ++++ append.c | 132 ++++++++++++++ ar.c | 140 +++++++++++++++ ar.h | 42 +++++ conv.c | 19 ++ delete.c | 183 +++++++++++++++++++ display.c | 57 ++++++ extract.c | 121 +++++++++++++ lib.c | 228 +++++++++++++++++++++++ lib.h | 13 ++ ranlib.c | 487 ++++++++++++++++++++++++++++++++++++++++++++++++++ replace.c | 193 ++++++++++++++++++++ report.c | 149 +++++++++++++++ report.h | 25 +++ 19 files changed, 1944 insertions(+) create mode 100644 LICENSE create mode 100644 Makefile.p32 create mode 100644 Makefile.pdw create mode 100644 Makefile.unix create mode 100644 Makefile.w32 create mode 100644 README.md create mode 100644 append.c create mode 100644 ar.c create mode 100644 ar.h create mode 100644 conv.c create mode 100644 delete.c create mode 100644 display.c create mode 100644 extract.c create mode 100644 lib.c create mode 100644 lib.h create mode 100644 ranlib.c create mode 100644 replace.c create mode 100644 report.c create mode 100644 report.h diff --git a/LICENSE b/LICENSE new file mode 100644 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.p32 b/Makefile.p32 new file mode 100644 index 0000000..31a532b --- /dev/null +++ b/Makefile.p32 @@ -0,0 +1,25 @@ +#****************************************************************************** +# @file Makefile.p32 +#****************************************************************************** +AS=as386 +CC=gcc386 +LD=ld386 + +COPTS=-S -O2 -fno-common -ansi -I. -I./include -I../pdos/pdpclib -I../pdos/src -D__PDOS386__ -D__32BIT__ -D__NOBIVA__ -D__PDOS__ -Wall -Werror -ansi -m32 -pedantic +COBJ=append.o ar.o conv.o delete.o display.o extract.o lib.o ranlib.o replace.o report.o + +all: clean sar.exe + +sar.exe: $(COBJ) + $(LD) -s -o sar.exe ../pdos/pdpclib/pdosst32.o $(COBJ) ../pdos/pdpclib/pdos.a + +.c.o: + $(CC) $(COPTS) -o $*.s $< + $(AS) -o $@ $*.s + rm -f $*.s + +clean: + for %f in ($(COBJ)) do ( rm -f %f ) + + rm -f sar + rm -f sar.exe diff --git a/Makefile.pdw b/Makefile.pdw new file mode 100644 index 0000000..20391da --- /dev/null +++ b/Makefile.pdw @@ -0,0 +1,25 @@ +#****************************************************************************** +# @file Makefile.pdw +#****************************************************************************** +AS=aswin +CC=gccwin +LD=ldwin + +COPTS=-S -O2 -fno-common -ansi -I. -I./include -I../pdos/pdpclib -I../pdos/src -D__WIN32__ -D__NOBIVA__ -D__PDOS__ -Wall -Werror -ansi -m32 -pedantic +COBJ=append.o ar.o conv.o delete.o display.o extract.o lib.o ranlib.o replace.o report.o + +all: clean sar.exe + +sar.exe: $(COBJ) + $(LD) -s -o sar.exe ../pdos/pdpclib/w32start.o $(COBJ) ../pdos/pdpclib/msvcrt.a ../pdos/src/kernel32.a + +.c.o: + $(CC) $(COPTS) -o $*.s $< + $(AS) -o $@ $*.s + rm -f $*.s + +clean: + for %f in ($(COBJ)) do ( rm -f %f ) + + rm -f sar + rm -f sar.exe diff --git a/Makefile.unix b/Makefile.unix new file mode 100644 index 0000000..12d6638 --- /dev/null +++ b/Makefile.unix @@ -0,0 +1,28 @@ +#****************************************************************************** +# @file Makefile.unix +#****************************************************************************** +OBJDIR ?= $(CURDIR) +SRCDIR ?= $(CURDIR) + +VPATH := $(SRCDIR) + +CC := gcc +CFLAGS := -D_FILE_OFFSET_BITS=64 -I$(OBJDIR) -I$(SRCDIR)/include -O2 -Wall -Werror -Wextra -ansi -pedantic -std=c90 + +CSRC := append.c ar.c conv.c delete.c display.c extract.c lib.c ranlib.c replace.c report.c + +ifeq ($(OS), Windows_NT) +all: sar.exe + +sar.exe: $(CSRC) + $(CC) $(CFLAGS) -o $@ $^ +else +all: sar + +sar: $(CSRC) + $(CC) $(CFLAGS) -o $@ $^ +endif + +clean: + if [ -f sar ]; then rm -rf sar; fi + if [ -f sar.exe ]; then rm -rf sar.exe; fi diff --git a/Makefile.w32 b/Makefile.w32 new file mode 100644 index 0000000..2b71ff5 --- /dev/null +++ b/Makefile.w32 @@ -0,0 +1,21 @@ +#****************************************************************************** +# @file Makefile.w32 +#****************************************************************************** +OBJDIR ?= $(CURDIR) +SRCDIR ?= $(CURDIR) + +VPATH := $(SRCDIR) + +CC := gcc +CFLAGS := -D_FILE_OFFSET_BITS=64 -I$(OBJDIR) -I$(SRCDIR)/include -O2 -Wall -Werror -Wextra -ansi -pedantic -std=c90 + +CSRC := append.c ar.c conv.c delete.c display.c extract.c lib.c ranlib.c replace.c report.c + +all: sar.exe + +clean: + if exist sar ( del /q sar ) + if exist sar.exe ( del /q sar.exe ) + +sar.exe: $(CSRC) + $(CC) $(CFLAGS) -o $@ $^ diff --git a/README.md b/README.md new file mode 100644 index 0000000..d4867ce --- /dev/null +++ b/README.md @@ -0,0 +1,32 @@ +## What is sar? + + Small Archiver (SAR) is a very small archiver to create, extract + and modify archives. + +## Licesne + + All source code is Public Domain. + +## Obtain the source code + + git clone https://git.candlhat.org/sar.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/append.c b/append.c new file mode 100644 index 0000000..8a72a7c --- /dev/null +++ b/append.c @@ -0,0 +1,132 @@ +/****************************************************************************** + * @file append.c + *****************************************************************************/ +#include +#include +#include + +#include "ar.h" +#include "lib.h" +#include "report.h" + +void append (FILE *ofp, const char *fname) { + + FILE *tfp; + + struct ar_header header; + char temp[17]; + + const char *name = fname; + char *p, *contents; + + int need_newline = 0; + long bytes, len, read; + + if ((tfp = fopen (fname, "r+b")) == NULL) { + + report_at (program_name, 0, REPORT_ERROR, "failed to open %s", fname); + return; + + } + + memset (temp, 0x20, 16); + temp[0] = '0'; + + if ((p = strrchr (fname, '/'))) { + name = (p + 1); + } + + len = strlen (name); + + if (len > 16) { + len = 16; + } + + memcpy (header.name, name, len); + + while (len < 16) { + header.name[len++] = 0x20; + } + + memcpy (header.mtime, temp, 12); + memcpy (header.owner, temp, 6); + memcpy (header.group, temp, 6); + memcpy (header.mode, temp, 8); + + fseek (tfp, 0, SEEK_END); + bytes = ftell (tfp); + + len = sprintf (temp, "%ld", bytes); + temp[len] = 0x20; + + memcpy (header.size, temp, 10); + + header.endsig[0] = 0x60; + header.endsig[1] = 0x0A; + + need_newline = (bytes % 2); + + if (fwrite (&header, sizeof (header), 1, ofp) != 1) { + + fclose (tfp); + + report_at (program_name, 0, REPORT_ERROR, "failed whilst writing header"); + return; + + } + + contents = xmalloc (512); + fseek (tfp, 0, SEEK_SET); + + for (;;) { + + if (bytes == 0 || feof (tfp)) { + break; + } else if (bytes >= 512) { + read = 512; + } else { + read = bytes; + } + + if (fread (contents, read, 1, tfp) != 1) { + + free (contents); + fclose (tfp); + + report_at (program_name, 0, REPORT_ERROR, "failed whilst reading %s", fname); + return; + + } + + bytes -= read; + + if (fwrite (contents, read, 1, ofp) != 1) { + + free (contents); + fclose (tfp); + + report_at (program_name, 0, REPORT_ERROR, "failed whilst writing %s to archive", fname); + return; + + } + + } + + free (contents); + + if (need_newline) { + + temp[0] = 0x0A; + + if (fwrite (temp, 1, 1, ofp) != 1) { + + fclose (tfp); + exit (EXIT_FAILURE); + + } + + } + + fclose (tfp); + +} diff --git a/ar.c b/ar.c new file mode 100644 index 0000000..868abda --- /dev/null +++ b/ar.c @@ -0,0 +1,140 @@ +/****************************************************************************** + * @file ar.c + *****************************************************************************/ +#include +#include +#include + +#include "ar.h" +#include "lib.h" +#include "report.h" + +static FILE *arfp = 0; + +struct ar_state *state = 0; +const char *program_name = 0; + +static void cleanup (void) { + + if (arfp) { fclose (arfp); } + + if (get_error_count () > 0) { + + if (state->outfile) { + remove (state->outfile); + } + + } + +} + +int main (int argc, char **argv) { + + char ar_magic[8]; + long i; + + atexit (cleanup); + + if (argc && *argv) { + + 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->append || state->replace) { + + if ((arfp = fopen (state->outfile, "r+b")) == NULL) { + + if ((arfp = fopen (state->outfile, "w+b")) == NULL) { + + report_at (program_name, 0, REPORT_ERROR, "failed to create %s", state->outfile); + return EXIT_FAILURE; + + } + + if (fwrite ("!\n", 8, 1, arfp) != 1) { + + fclose (arfp); + + report_at (program_name, 0, REPORT_ERROR, "failed whilst writing to %s", state->outfile); + return EXIT_FAILURE; + + } + + } + + fclose (arfp); + arfp = 0; + + } + + if ((arfp = fopen (state->outfile, "r+b")) == NULL) { + + report_at (program_name, 0, REPORT_ERROR, "failed to open %s", state->outfile); + return EXIT_FAILURE; + + } + + if (fread (ar_magic, 8, 1, arfp) != 1) { + + report_at (program_name, 0, REPORT_ERROR, "failed whilst reading %s", state->outfile); + return EXIT_FAILURE; + + } + + if (memcmp ("!\n", ar_magic, 8)) { + + report_at (program_name, 0, REPORT_ERROR, "%s is not a valid archive file", state->outfile); + return EXIT_FAILURE; + + } + + for (i = 0; i < state->nb_files; ++i) { + + if (state->append) { + + fseek (arfp, 0, SEEK_END); + append (arfp, state->files[i]); + + } else if (state->replace) { + + fseek (arfp, 8, SEEK_SET); + replace (arfp, state->files[i]); + + } else if (state->del) { + + fseek (arfp, 8, SEEK_SET); + delete (arfp, state->files[i]); + + } else if (state->extract) { + + fseek (arfp, 8, SEEK_SET); + extract (arfp, state->files[0]); + + } + + } + + if (state->display) { + + fseek (arfp, 8, SEEK_SET); + display (arfp); + + } else if (state->ranlib) { + + fseek (arfp, 8, SEEK_SET); + ranlib (arfp); + + } + + return EXIT_SUCCESS; + +} diff --git a/ar.h b/ar.h new file mode 100644 index 0000000..7e0459c --- /dev/null +++ b/ar.h @@ -0,0 +1,42 @@ +/****************************************************************************** + * @file ar.h + *****************************************************************************/ +#ifndef _AR_H +#define _AR_H + +struct ar_state { + + const char **files; + long nb_files; + + const char *outfile; + int append, del, display, extract, move, print, replace, ranlib; + +}; + +extern struct ar_state *state; +extern const char *program_name; + +struct ar_header { + + char name[16]; + char mtime[12]; + char owner[6]; + char group[6]; + char mode[8]; + char size[10]; + char endsig[2]; + +}; + +#include +unsigned long conv_dec (char *str, long max); + +void append (FILE *ofp, const char *fname); +void delete (FILE *arfp, const char *fname); +void display (FILE *arfp); +void extract (FILE *arfp, const char *fname); +void ranlib (FILE *arfp); +void replace (FILE *arfp, const char *fname); + +#endif /* _AR_H */ diff --git a/conv.c b/conv.c new file mode 100644 index 0000000..459194b --- /dev/null +++ b/conv.c @@ -0,0 +1,19 @@ +/****************************************************************************** + * @file conv.c + *****************************************************************************/ +#include "stdint.h" + +unsigned long conv_dec (char *str, long max) { + + unsigned long value = 0; + + while (*str != ' ' && max-- > 0) { + + value *= 10; + value += *str++ - '0'; + + } + + return value; + +} diff --git a/delete.c b/delete.c new file mode 100644 index 0000000..d8248fb --- /dev/null +++ b/delete.c @@ -0,0 +1,183 @@ +/****************************************************************************** + * @file delete.c + *****************************************************************************/ +#include +#include +#include + +#include "ar.h" +#include "lib.h" +#include "report.h" + +void delete (FILE *arfp, const char *fname) { + + FILE *tfp = tmpfile (); + long bytes, len, read; + + const char *name; + char temp[17], *p, *contents; + + if (fwrite ("!\x0A", 8, 1, tfp) != 1) { + + fclose (tfp); + + report_at (program_name, 0, REPORT_ERROR, "failed whist writing ar header"); + return; + + } + + name = fname; + + if ((p = strrchr (fname, '/'))) { + name = (p + 1); + } + + len = strlen (name); + + if (len > 16) { + len = 16; + } + + memcpy (temp, name, len); + temp[len] = '\0'; + + for (;;) { + + struct ar_header hdr; + + if (fread (&hdr, sizeof (hdr), 1, arfp) != 1) { + + if (feof (arfp)) { + break; + } + + report_at (program_name, 0, REPORT_ERROR, "failed whilst reading '%s'", state->outfile); + return; + + } + + bytes = conv_dec (hdr.size, 10); + + if (bytes % 2) { + bytes++; + } + + if (memcmp (hdr.name, "/", 1) == 0) { + + fseek (arfp, bytes, SEEK_CUR); + continue; + + } + + if (memcmp (hdr.name, temp, len) == 0) { + + fseek (arfp, bytes, SEEK_CUR); + continue; + + } + + if (fwrite (&hdr, sizeof (hdr), 1, tfp) != 1) { + + fclose (tfp); + + report_at (program_name, 0, REPORT_ERROR, "failed whilst writing header"); + exit (EXIT_FAILURE); + + } + + contents = xmalloc (512); + + for (;;) { + + if (bytes == 0 || feof (arfp)) { + break; + } else if (bytes >= 512) { + read = 512; + } else { + read = bytes; + } + + if (fread (contents, read, 1, arfp) != 1) { + + free (contents); + fclose (tfp); + + report_at (program_name, 0, REPORT_ERROR, "failed to read %ld bytes from %s", bytes, state->outfile); + exit (EXIT_FAILURE); + + } + + bytes -= read; + + if (fwrite (contents, read, 1, tfp) != 1) { + + free (contents); + fclose (tfp); + + report_at (program_name, 0, REPORT_ERROR, "failed to write temp file"); + exit (EXIT_FAILURE); + + } + + } + + free (contents); + + } + + fclose (arfp); + remove (state->outfile); + + if ((arfp = fopen (state->outfile, "w+b")) == NULL) { + + fclose (tfp); + + report_at (program_name, 0, REPORT_ERROR, "failed to open %s for writing", state->outfile); + exit (EXIT_FAILURE); + + } + + contents = xmalloc (512); + bytes = ftell (tfp); + + fseek (arfp, 0, SEEK_SET); + fseek (tfp, 0, SEEK_SET); + + for (;;) { + + if (bytes == 0 || feof (tfp)) { + break; + } else if (bytes >= 512) { + read = 512; + } else { + read = bytes; + } + + if (fread (contents, read, 1, tfp) != 1) { + + free (contents); + fclose (tfp); + + report_at (program_name, 0, REPORT_ERROR, "failed whilst reading temp file"); + return; + + } + + bytes -= read; + + if (fwrite (contents, read, 1, arfp) != 1) { + + free (contents); + fclose (tfp); + + report_at (program_name, 0, REPORT_ERROR, "failed whist writing %s", state->outfile); + exit (EXIT_FAILURE); + + } + + } + + free (contents); + fclose (tfp); + +} diff --git a/display.c b/display.c new file mode 100644 index 0000000..c25fd05 --- /dev/null +++ b/display.c @@ -0,0 +1,57 @@ +/****************************************************************************** + * @file display.c + *****************************************************************************/ +#include +#include + +#include "ar.h" +#include "report.h" + +void display (FILE *arfp) { + + char temp[17] = { 0 }; + int i; + + for (;;) { + + struct ar_header hdr; + long bytes; + + if (fread (&hdr, sizeof (hdr), 1, arfp) != 1) { + + if (feof (arfp)) { + break; + } + + report_at (program_name, 0, REPORT_ERROR, "failed whilst reading '%s'", state->outfile); + return; + + } + + bytes = conv_dec (hdr.size, 10); + + if (bytes % 2) { + bytes++; + } + + fseek (arfp, bytes, SEEK_CUR); + + if (memcmp (hdr.name, "/", 1) == 0) { + continue; + } + + memcpy (temp, hdr.name, 16); + + for (i = 16; i >= 0; --i) { + + if (temp[i] == 0x20) { + temp[i] = '\0'; + } + + } + + printf ("%s\n", temp); + + } + +} diff --git a/extract.c b/extract.c new file mode 100644 index 0000000..bf0d79b --- /dev/null +++ b/extract.c @@ -0,0 +1,121 @@ +/****************************************************************************** + * @file extract.c + *****************************************************************************/ +#include +#include +#include + +#include "ar.h" +#include "lib.h" +#include "report.h" + +void extract (FILE *arfp, const char *fname) { + + FILE *ofp; + long bytes, len, read; + + const char *name; + char temp[17], *p, *contents; + + name = fname; + + if ((p = strrchr (fname, '/'))) { + name = (p + 1); + } + + len = strlen (name); + + if (len > 16) { + len = 16; + } + + memcpy (temp, name, len); + temp[len] = '\0'; + + for (;;) { + + struct ar_header hdr; + + if (fread (&hdr, sizeof (hdr), 1, arfp) != 1) { + + if (feof (arfp)) { + break; + } + + report_at (program_name, 0, REPORT_ERROR, "failed whilst reading '%s'", state->outfile); + return; + + } + + bytes = conv_dec (hdr.size, 10); + + if (memcmp (hdr.name, "/", 1) == 0) { + + if (bytes % 2) { + bytes++; + } + + fseek (arfp, bytes, SEEK_CUR); + continue; + + } + + if (memcmp (hdr.name, temp, len) != 0) { + + if (bytes % 2) { + bytes++; + } + + fseek (arfp, bytes, SEEK_CUR); + continue; + + } + + if ((ofp = fopen (temp, "w+b")) == NULL) { + + report_at (program_name, 0, REPORT_ERROR, "failed to open %s for writing", temp); + exit (EXIT_FAILURE); + + } + + contents = xmalloc (512); + + for (;;) { + + if (bytes == 0 || feof (arfp)) { + break; + } else if (bytes >= 512) { + read = 512; + } else { + read = bytes; + } + + if (fread (contents, read, 1, arfp) != 1) { + + free (contents); + fclose (ofp); + + report_at (program_name, 0, REPORT_ERROR, "failed to read %ld bytes from %s", bytes, state->outfile); + exit (EXIT_FAILURE); + + } + + bytes -= read; + + if (fwrite (contents, read, 1, ofp) != 1) { + + free (contents); + fclose (ofp); + + report_at (program_name, 0, REPORT_ERROR, "failed to write %s file", temp); + exit (EXIT_FAILURE); + + } + + } + + free (contents); + + } + +} diff --git a/lib.c b/lib.c new file mode 100644 index 0000000..c51c148 --- /dev/null +++ b/lib.c @@ -0,0 +1,228 @@ +/****************************************************************************** + * @file lib.c + *****************************************************************************/ +#include +#include +#include + +#include "ar.h" +#include "lib.h" +#include "report.h" + +static void print_usage (void) { + + if (program_name) { + + fprintf (stderr, "Usage: %s command archive-file file...\n\n", program_name); + fprintf (stderr, "Commands:\n\n"); + + fprintf (stderr, " d - delete file(s) from the archive\n"); + fprintf (stderr, " q - quick append file(s) to the archive\n"); + fprintf (stderr, " r - replace existing or insert new file(s) into the archive\n"); + fprintf (stderr, " s - act as ranlib\n"); + fprintf (stderr, " t - display contents of the archive\n"); + fprintf (stderr, " x - extract file(s) from the archive\n"); + + fprintf (stderr, "\n"); + + } + +} + +static void dynarray_add (void *ptab, long *nb_ptr, void *data) { + + long 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; + +} + +char *xstrdup (const char *__p) { + + char *p = xmalloc (strlen (__p) + 1); + + strcpy (p, __p); + return p; + +} + +void parse_args (int argc, char **argv, int optind) { + + const char *r; + int opt; + + if (argc <= optind) { + + print_usage (); + exit (EXIT_SUCCESS); + + } + + r = argv[optind++]; + +check_options: + + if (r[0] == '-') { + ++r; + } + + while (*r != '\0') { + + char ch = *r++; + + if (ch == 'd') { + + state->del++; + continue; + + } + + if (ch == 'm') { + + state->move++; + continue; + + } + + if (ch == 'p') { + + state->print++; + continue; + + } + + if (ch == 'q') { + + state->append++; + continue; + + } + + if (ch == 'r') { + + state->replace++; + continue; + + } + + if (ch == 's') { + + state->ranlib++; + continue; + + } + + if (ch == 't') { + + state->display++; + continue; + + } + + if (ch == 'x') { + + state->extract++; + continue; + + } + + report_at (program_name, 0, REPORT_ERROR, "invalid option -- '%c'", ch); + + print_usage (); + exit (EXIT_SUCCESS); + + } + + if ((r = argv[optind++])) { + + if (*r == '-') { + goto check_options; + } + + } + + if ((opt = state->append + state->del + state->display + state->extract + state->move + state->print + state->ranlib + state->replace) != 1) { + + if (opt > 1) { + report_at (program_name, 0, REPORT_ERROR, "more than one option provided"); + } + + print_usage (); + exit (EXIT_SUCCESS); + + } + + if (!r) { + + print_usage (); + exit (EXIT_SUCCESS); + + } + + state->outfile = xstrdup (r); + + if (state->display || state->ranlib) { + return; + } + + if (optind >= argc) { + + print_usage (); + exit (EXIT_SUCCESS); + + } + + while (optind < argc) { + dynarray_add (&state->files, &state->nb_files, xstrdup (argv[optind++])); + } + +} + +void *xmalloc (unsigned long __size) { + + void *ptr = malloc (__size); + + if (!ptr && __size) { + + report_at (program_name, 0, REPORT_ERROR, "memory full (malloc)"); + exit (EXIT_FAILURE); + + } + + memset (ptr, 0, __size); + return ptr; + +} + +void *xrealloc (void *__ptr, unsigned long __size) { + + void *ptr = realloc (__ptr, __size); + + if (!ptr && __size) { + + report_at (program_name, 0, REPORT_ERROR, "memory full (realloc)"); + exit (EXIT_FAILURE); + + } + + return ptr; + +} diff --git a/lib.h b/lib.h new file mode 100644 index 0000000..ec8cb8b --- /dev/null +++ b/lib.h @@ -0,0 +1,13 @@ +/****************************************************************************** + * @file lib.h + *****************************************************************************/ +#ifndef _LIB_H +#define _LIB_H + +char *xstrdup (const char *__p); +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/ranlib.c b/ranlib.c new file mode 100644 index 0000000..887b6f5 --- /dev/null +++ b/ranlib.c @@ -0,0 +1,487 @@ +/****************************************************************************** + * @file ranlib.c + *****************************************************************************/ +#include +#include +#include +#include +#include + +#include "ar.h" +#include "lib.h" +#include "report.h" + +#if INT_MAX == 32767 +typedef signed long int32_t; +#else +typedef signed int int32_t; +#endif + +#define GET_INT32(arr) ((long) arr[0] | (((long) arr[1]) << 8) | (((long) arr[2]) << 16) | (((long) arr[3]) << 24)) + +#define GET_UINT16(arr) ((unsigned long) arr[0] | (((unsigned long) arr[1]) << 8)) +#define GET_UINT32(arr) ((unsigned long) arr[0] | (((unsigned long) arr[1]) << 8) | (((unsigned long) arr[2]) << 16) | (((unsigned long) arr[3]) << 24)) + +struct aout_exec { + + unsigned char a_info[4]; + unsigned char a_text[4]; + unsigned char a_data[4]; + unsigned char a_bss[4]; + unsigned char a_syms[4]; + unsigned char a_entry[4]; + unsigned char a_trsize[4]; + unsigned char a_drsize[4]; + +}; + +struct aout_nlist { + + unsigned char n_strx[4]; + unsigned char n_type; + + unsigned char n_other; + unsigned char n_desc[2]; + + unsigned char n_value[4]; + +}; + +struct nlist { + + unsigned char n_strx[4]; + unsigned char n_type; + + unsigned char n_value[4]; + +}; + +struct strtab { + + const char *name; + long length, offset; + +}; + +struct gstrtab { + + long count, max; + struct strtab *strtabs; + +}; + +static struct gstrtab gstrtab = { 0, 64, NULL }; + +static int add_strtab (struct gstrtab *gstrtab, struct strtab *strtab) { + + if (gstrtab->strtabs == NULL) { + + if ((gstrtab->strtabs = malloc (gstrtab->max * sizeof (*strtab))) == NULL) { + return 1; + } + + } + + if (gstrtab->count >= gstrtab->max) { + + void *tmp; + + gstrtab->max *= 2; + + if ((tmp = realloc (gstrtab->strtabs, gstrtab->max * sizeof (*strtab))) == NULL) { + return 1; + } + + gstrtab->strtabs = tmp; + + } + + gstrtab->strtabs[gstrtab->count] = *strtab; + gstrtab->count++; + + return 0; + +} + +static void aout_get_symbols (void *object, long offset) { + + struct aout_exec *hdr = (struct aout_exec *) object; + + long sym_start = sizeof (*hdr) + GET_UINT32 (hdr->a_text) + GET_UINT32 (hdr->a_data) + GET_UINT32 (hdr->a_trsize) + GET_UINT32 (hdr->a_drsize); + long strtab_start = sym_start + GET_UINT32 (hdr->a_syms); + + while (sym_start < strtab_start) { + + if (hdr->a_info[0] == 0x07) { + + struct aout_nlist nlist; + memcpy (&nlist, (char *) object + sym_start, sizeof (nlist)); + + if (nlist.n_type == 5 || nlist.n_type == 7 || nlist.n_type == 9) { + + struct strtab *strtab; + char *symname = (char *) object + strtab_start + GET_INT32 (nlist.n_strx); + + strtab = xmalloc (sizeof (*strtab)); + strtab->length = strlen (symname); + + strtab->name = xstrdup (symname); + strtab->offset = offset; + + add_strtab (&gstrtab, strtab); + + } + + sym_start += sizeof (nlist); + + } else { + + struct nlist nlist; + memcpy (&nlist, (char *) object + sym_start, sizeof (nlist)); + + if (nlist.n_type == 5 || nlist.n_type == 7 || nlist.n_type == 9) { + + struct strtab *strtab; + char *symname = (char *) object + strtab_start + GET_INT32 (nlist.n_strx); + + strtab = xmalloc (sizeof (*strtab)); + strtab->length = strlen (symname); + + strtab->name = xstrdup (symname); + strtab->offset = offset; + + add_strtab (&gstrtab, strtab); + + } + + sym_start += sizeof (nlist); + + } + + } + +} + +void ranlib (FILE *arfp) { + + FILE *tfp = tmpfile (); + + struct ar_header header; + int32_t bytes, i, j, len, read, val; + + unsigned char *object; + void *contents; + + char temp[16]; + long offset = 0; + + for (;;) { + + struct ar_header hdr; + long bytes; + + if (fread (&hdr, sizeof (hdr), 1, arfp) != 1) { + + if (feof (arfp)) { + break; + } + + report_at (program_name, 0, REPORT_ERROR, "failed whilst reading '%s'", state->outfile); + return; + + } + + bytes = conv_dec (hdr.size, 10); + + if (bytes % 2) { + bytes++; + } + + if (memcmp (hdr.name, "/", 1) == 0) { + + fseek (arfp, bytes, SEEK_CUR); + continue; + + } + + object = xmalloc (bytes); + + if (fread (object, bytes, 1, arfp) != 1) { + + free (object); + + report_at (program_name, 0, REPORT_ERROR, "failed to read %ld bytes from %s", bytes, state->outfile); + exit (EXIT_FAILURE); + + + } + + if (!((object[0] == 0x39 || object[0] == 0x07) && object[1] == 0x01 && object[2] == 0x64 && object[3] == 0x00)) { + + free (object); + + offset += sizeof (hdr); + offset += bytes; + + fseek (arfp, bytes, SEEK_CUR); + continue; + + } + + if ((object[0] == 0x39 || object[0] == 0x07) && object[1] == 0x01 && object[2] == 0x64 && object[3] == 0x00) { + aout_get_symbols (object, offset + 8); + } + + free (object); + + offset += sizeof (hdr); + offset += bytes; + + } + + fseek (arfp, 8, SEEK_SET); + bytes = 0; + + for (i = 0; i < gstrtab.count; ++i) { + bytes += gstrtab.strtabs[i].length + 5; + } + + for (i = 0; i < gstrtab.count; ++i) { + + gstrtab.strtabs[i].offset += bytes; + + if (bytes % 2) { + gstrtab.strtabs[i].offset++; + } + + } + + if (fwrite ("!\x0A", 8, 1, tfp) != 1) { + + fclose (tfp); + + report_at (program_name, 0, REPORT_ERROR, "failed whist writing ar header"); + return; + + } + + memset (temp, 0x20, 16); + temp[0] = '0'; + + len = 1; + memcpy (header.name, "/", len); + + while (len < 16) { + header.name[len++] = 0x20; + } + + memcpy (header.mtime, temp, 12); + memcpy (header.owner, temp, 6); + memcpy (header.group, temp, 6); + memcpy (header.mode, temp, 8); + +#if INT_MAX == 32767 + len = sprintf (temp, "%ld", bytes + 4); +#else + len = sprintf (temp, "%d", bytes + 4); +#endif + + temp[len] = 0x20; + memcpy (header.size, temp, 10); + + header.endsig[0] = 0x60; + header.endsig[1] = 0x0A; + + if (fwrite (&header, sizeof (header), 1, tfp) != 1) { + + fclose (tfp); + + report_at (program_name, 0, REPORT_ERROR, "failed whilst writing header"); + return; + + } + + val = gstrtab.count; + + for (i = 0; i < 4; ++i) { + temp[4 - 1 - i] = (val >> (CHAR_BIT * i)) & UCHAR_MAX; + } + + if (fwrite (temp, 4, 1, tfp) != 1) { + exit (EXIT_FAILURE); + } + + for (i = 0; i < gstrtab.count; ++i) { + + val = gstrtab.strtabs[i].offset + 4 + sizeof (header); + + for (j = 0; j < 4; ++j) { + temp[4 - 1 - j] = (val >> (CHAR_BIT * j)) & UCHAR_MAX; + } + + if (fwrite (temp, 4, 1, tfp) != 1) { + exit (EXIT_FAILURE); + } + + } + + for (i = 0; i < gstrtab.count; ++i) { + + const char *name = gstrtab.strtabs[i].name; + long length = gstrtab.strtabs[i].length; + + if (fwrite (name, length, 1, tfp) != 1) { + exit (EXIT_FAILURE); + } + + temp[0] = '\0'; + + if (fwrite (temp, 1, 1, tfp) != 1) { + exit (EXIT_FAILURE); + } + + } + + if (bytes % 2) { + + temp[0] = '\0'; + + if (fwrite (temp, 1, 1, tfp) != 1) { + exit (EXIT_FAILURE); + } + + } + + for (;;) { + + struct ar_header hdr; + + if (fread (&hdr, sizeof (hdr), 1, arfp) != 1) { + + if (feof (arfp)) { + break; + } + + report_at (program_name, 0, REPORT_ERROR, "failed whilst reading '%s'", state->outfile); + return; + + } + + bytes = conv_dec (hdr.size, 10); + + if (bytes % 2) { + bytes++; + } + + if (memcmp (hdr.name, "/", 1) == 0) { + + fseek (arfp, bytes, SEEK_CUR); + continue; + + } + + if (fwrite (&hdr, sizeof (hdr), 1, tfp) != 1) { + + fclose (tfp); + + report_at (program_name, 0, REPORT_ERROR, "failed whilst writing header"); + exit (EXIT_FAILURE); + + } + + contents = xmalloc (512); + + for (;;) { + + if (bytes == 0 || feof (arfp)) { + break; + } else if (bytes >= 512) { + read = 512; + } else { + read = bytes; + } + + if (fread (contents, read, 1, arfp) != 1) { + + free (contents); + fclose (tfp); + + report_at (program_name, 0, REPORT_ERROR, "failed to read %ld bytes from %s", bytes, state->outfile); + exit (EXIT_FAILURE); + + } + + bytes -= read; + + if (fwrite (contents, read, 1, tfp) != 1) { + + free (contents); + fclose (tfp); + + report_at (program_name, 0, REPORT_ERROR, "failed to write temp file"); + exit (EXIT_FAILURE); + + } + + } + + free (contents); + + } + + fclose (arfp); + remove (state->outfile); + + if ((arfp = fopen (state->outfile, "w+b")) == NULL) { + + fclose (tfp); + + report_at (program_name, 0, REPORT_ERROR, "failed to open %s for writing", state->outfile); + exit (EXIT_FAILURE); + + } + + contents = xmalloc (512); + bytes = ftell (tfp); + + fseek (arfp, 0, SEEK_SET); + fseek (tfp, 0, SEEK_SET); + + for (;;) { + + if (bytes == 0 || feof (tfp)) { + break; + } else if (bytes >= 512) { + read = 512; + } else { + read = bytes; + } + + if (fread (contents, read, 1, tfp) != 1) { + + free (contents); + fclose (tfp); + + report_at (program_name, 0, REPORT_ERROR, "failed whilst reading temp file"); + return; + + } + + bytes -= read; + + if (fwrite (contents, read, 1, arfp) != 1) { + + free (contents); + fclose (tfp); + + report_at (program_name, 0, REPORT_ERROR, "failed whist writing %s", state->outfile); + exit (EXIT_FAILURE); + + } + + } + + free (contents); + fclose (tfp); + +} diff --git a/replace.c b/replace.c new file mode 100644 index 0000000..31e5dc2 --- /dev/null +++ b/replace.c @@ -0,0 +1,193 @@ +/****************************************************************************** + * @file replace.c + *****************************************************************************/ +#include +#include +#include + +#include "ar.h" +#include "lib.h" +#include "report.h" + +void replace (FILE *arfp, const char *fname) { + + FILE *tfp = tmpfile (); + long bytes, len, read; + + const char *name; + char temp[17], *p, *contents; + + if (fwrite ("!\x0A", 8, 1, tfp) != 1) { + + fclose (tfp); + + report_at (program_name, 0, REPORT_ERROR, "failed whist writing ar header"); + return; + + } + + name = fname; + + if ((p = strrchr (fname, '/'))) { + name = (p + 1); + } + + len = strlen (name); + + if (len > 16) { + len = 16; + } + + memcpy (temp, name, len); + temp[len] = '\0'; + + state->append = 1; + + for (;;) { + + struct ar_header hdr; + + if (fread (&hdr, sizeof (hdr), 1, arfp) != 1) { + + if (feof (arfp)) { + break; + } + + report_at (program_name, 0, REPORT_ERROR, "failed whilst reading '%s'", state->outfile); + return; + + } + + bytes = conv_dec (hdr.size, 10); + + if (bytes % 2) { + bytes++; + } + + if (memcmp (hdr.name, "/", 1) == 0) { + + fseek (arfp, bytes, SEEK_CUR); + continue; + + } + + if (memcmp (hdr.name, temp, len) == 0) { + + state->append = 0; + append (tfp, fname); + + fseek (arfp, bytes, SEEK_CUR); + continue; + + } + + if (fwrite (&hdr, sizeof (hdr), 1, tfp) != 1) { + + fclose (tfp); + + report_at (program_name, 0, REPORT_ERROR, "failed whilst writing header"); + exit (EXIT_FAILURE); + + } + + contents = xmalloc (512); + + for (;;) { + + if (bytes == 0 || feof (arfp)) { + break; + } else if (bytes >= 512) { + read = 512;; + } else { + read = bytes; + } + + if (fread (contents, read, 1, arfp) != 1) { + + free (contents); + + report_at (program_name, 0, REPORT_ERROR, "failed to read %ld bytes from %s", bytes, state->outfile); + exit (EXIT_FAILURE); + + } + + bytes -= read; + + if (fwrite (contents, read, 1, tfp) != 1) { + + free (contents); + + report_at (program_name, 0, REPORT_ERROR, "failed to write temp file"); + exit (EXIT_FAILURE); + + } + + } + + free (contents); + + } + + if (state->append) { + + state->append = 0; + append (tfp, fname); + + } + + fclose (arfp); + remove (state->outfile); + + if ((arfp = fopen (state->outfile, "w+b")) == NULL) { + + fclose (tfp); + + report_at (program_name, 0, REPORT_ERROR, "failed to open %s", state->outfile); + exit (EXIT_FAILURE); + + } + + contents = xmalloc (512); + bytes = ftell (tfp); + + fseek (arfp, 0, SEEK_SET); + fseek (tfp, 0, SEEK_SET); + + for (;;) { + + if (bytes == 0 || feof (tfp)) { + break; + } else if (bytes >= 512) { + read = 512; + } else { + read = bytes; + } + + if (fread (contents, read, 1, tfp) != 1) { + + free (contents); + fclose (tfp); + + report_at (program_name, 0, REPORT_ERROR, "failed whilst reading temp file"); + return; + + } + + bytes -= read; + + if (fwrite (contents, read, 1, arfp) != 1) { + + free (contents); + fclose (tfp); + + report_at (program_name, 0, REPORT_ERROR, "failed whist writing %s", state->outfile); + exit (EXIT_FAILURE); + + } + + } + + free (contents); + fclose (tfp); + +} diff --git a/report.c b/report.c new file mode 100644 index 0000000..fb010ce --- /dev/null +++ b/report.c @@ -0,0 +1,149 @@ +/****************************************************************************** + * @file report.c + *****************************************************************************/ +#include +#include +#include + +#include "report.h" +unsigned int 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 int lineno, unsigned int idx, int type, const char *fmt, va_list ap) { + + if (filename) { + + if (lineno == 0) { + fprintf (stderr, "%s: ", filename); + } else { + fprintf (stderr, "%s:", filename); + } + + } + + if (lineno > 0) { + + if (idx == 0) { + fprintf (stderr, "%u: ", lineno); + } else { + fprintf (stderr, "%u:", lineno); + } + + } + + if (idx > 0) { + fprintf (stderr, "%u: ", 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 int get_error_count (void) { + return errors; +} + +void report_at (const char *filename, unsigned int lineno, int 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 100644 index 0000000..8e694dd --- /dev/null +++ b/report.h @@ -0,0 +1,25 @@ +/****************************************************************************** + * @file report.h + *****************************************************************************/ +#ifndef _REPORT_H +#define _REPORT_H + +#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 + +#define REPORT_WARNING 0 +#define REPORT_ERROR 1 +#define REPORT_FATAL_ERROR 3 +#define REPORT_INTERNAL_ERROR 4 + +unsigned int get_error_count (void); +void report_at (const char *filename, unsigned int lineno, int type, const char *fmt, ...); + +#endif /* _REPORT_H */ -- 2.34.1