From: Robert Pengelly Date: Mon, 3 Jun 2024 03:34:19 +0000 (+0100) Subject: New server X-Git-Url: https://git.candlhat.org/?a=commitdiff_plain;h=d7ae5ccfda2892d7804ab86039a740547c089158;p=sar.git New server --- d7ae5ccfda2892d7804ab86039a740547c089158 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 */