--- /dev/null
+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 <https://unlicense.org>
--- /dev/null
+#******************************************************************************
+# @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
--- /dev/null
+#******************************************************************************
+# @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
--- /dev/null
+#******************************************************************************
+# @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
--- /dev/null
+#******************************************************************************
+# @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 $@ $^
--- /dev/null
+## 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.
--- /dev/null
+/******************************************************************************
+ * @file append.c
+ *****************************************************************************/
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#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);
+
+}
--- /dev/null
+/******************************************************************************
+ * @file ar.c
+ *****************************************************************************/
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#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 ("!<arch>\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 ("!<arch>\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;
+
+}
--- /dev/null
+/******************************************************************************
+ * @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 <stdio.h>
+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 */
--- /dev/null
+/******************************************************************************
+ * @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;
+
+}
--- /dev/null
+/******************************************************************************
+ * @file delete.c
+ *****************************************************************************/
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#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 ("!<arch>\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);
+
+}
--- /dev/null
+/******************************************************************************
+ * @file display.c
+ *****************************************************************************/
+#include <stdio.h>
+#include <string.h>
+
+#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);
+
+ }
+
+}
--- /dev/null
+/******************************************************************************
+ * @file extract.c
+ *****************************************************************************/
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#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);
+
+ }
+
+}
--- /dev/null
+/******************************************************************************
+ * @file lib.c
+ *****************************************************************************/
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#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;
+
+}
--- /dev/null
+/******************************************************************************
+ * @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 */
--- /dev/null
+/******************************************************************************
+ * @file ranlib.c
+ *****************************************************************************/
+#include <limits.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#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 ("!<arch>\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);
+
+}
--- /dev/null
+/******************************************************************************
+ * @file replace.c
+ *****************************************************************************/
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#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 ("!<arch>\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);
+
+}
--- /dev/null
+/******************************************************************************
+ * @file report.c
+ *****************************************************************************/
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "report.h"
+unsigned int errors = 0;
+
+#ifndef __PDOS__
+#if defined (_WIN32)
+# include <windows.h>
+static int OriginalConsoleColor = -1;
+#endif
+
+static void reset_console_color (void) {
+
+#if defined (_WIN32)
+
+ HANDLE hStdError = GetStdHandle (STD_ERROR_HANDLE);
+
+ if (OriginalConsoleColor == -1) { return; }
+
+ SetConsoleTextAttribute (hStdError, OriginalConsoleColor);
+ OriginalConsoleColor = -1;
+
+#else
+
+ fprintf (stderr, "\033[0m");
+
+#endif
+
+}
+
+static void set_console_color (int color) {
+
+#if defined (_WIN32)
+
+ HANDLE hStdError = GetStdHandle (STD_ERROR_HANDLE);
+ WORD wColor;
+
+ if (OriginalConsoleColor == -1) {
+
+ CONSOLE_SCREEN_BUFFER_INFO csbi;
+
+ if (!GetConsoleScreenBufferInfo (hStdError, &csbi)) {
+ return;
+ }
+
+ OriginalConsoleColor = csbi.wAttributes;
+
+ }
+
+ wColor = (OriginalConsoleColor & 0xF0) + (color & 0xF);
+ SetConsoleTextAttribute (hStdError, wColor);
+
+#else
+
+ fprintf (stderr, "\033[%dm", color);
+
+#endif
+
+}
+#endif
+
+static void output_message (const char *filename, unsigned 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);
+
+}
--- /dev/null
+/******************************************************************************
+ * @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 */