view checksum/checksum.c @ 14:a02e9434072e

Fix friend declaration for gcc10. XXX: did not check it against the IBM compiler, might end up needing XXX: to be conditional.
author David A. Holland
date Tue, 31 May 2022 00:59:42 -0400
parents 13d2b8934445
children
line wrap: on
line source

/*
 * AnaGram, A System for Syntax Directed Programming
 * Copyright 2006 David A. Holland. All Rights Reserved.
 * See the file COPYING for license and usage terms.
 */

/*
 * checksum - compute checksums of key files.
 * usage: checksum outputprefix symbolic-name filename
 *
 * Outputs to two files, one with the checksum info and one
 * the control file for insertsums.
 *
 * In each binary searches for a sums patch area, which is a
 * region of size TARGETSIZE beginning with the string TARGETLABEL.
 * This region is excluded from the sum and reported to insertsums.
 *
 * XXX the definitions of TARGETSIZE and TARGETLABEL should be shared
 * with the ag code.
 *
 * This module is new code as of June 2006 and should be much more
 * portable than the old stuff.
 */

#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <unistd.h>
#include <fcntl.h>
#include <getopt.h>
#include <errno.h>

#include "bits.h"

#ifndef O_BINARY
#define O_BINARY 0
#define O_TEXT   0
#endif

#define TARGETSIZE	512
#define TARGETLABEL	"Checksum data:\n"

static FILE *datfile, *ctlfile;

#if 0
/* The file extensions depend on the platform we're building for. */
typedef enum {
  EXEEXT,
  SHLIBEXT,
  HLPEXT,
} whichext;

/* Information about files we checksum. */
struct fileinfo {
  const char *basename;
  whichext ext;
  int istarget;
  const char *tag;
};

/* These are the files. */
static const struct fileinfo files[] = {
  { "ag",      EXEEXT,   0, "ag" },
  { "agcl",    EXEEXT,   0, "agcl" },
  { "ag1",     SHLIBEXT, 1, "ag1" },
  { "AnaGram", HLPEXT,   0, "hlp" },
};
static const unsigned numfiles = sizeof(files) / sizeof(files[0]);
#endif

/* The name of this program, from argv[0]. */
static const char *me;

////////////////////////////////////////////////////////////

static off_t filelength(int fd) {
  struct stat statbuf;
  fstat(fd, &statbuf);
  return statbuf.st_size;
}

static void setme(const char *av0) {
  me = strrchr(av0, '/');
  if (me) {
    me++;
  }
  else {
    me = av0;
  }
}

static void die(const char *fmt, ...) {
  va_list ap;
  fprintf(stderr, "%s: ", me);
  va_start(ap, fmt);
  vfprintf(stderr, fmt, ap);
  fputc('\n', stderr);
  exit(EXIT_FAILURE);
}

////////////////////////////////////////////////////////////

static void openoutput(const char *prefix) {
  char buf[128];

  snprintf(buf, sizeof(buf), "%s.dat", prefix);
  datfile = fopen(buf, "at");
  if (!datfile) {
    die("%s: fopen failed", buf);
  }

  snprintf(buf, sizeof(buf), "%s.ctl", prefix);
  ctlfile = fopen(buf, "at");
  if (!ctlfile) {
    die("%s: fopen failed", buf);
  }
}

static void closeoutput(void) {
  fclose(datfile);
  fclose(ctlfile);
}

////////////////////////////////////////////////////////////

/*
 * Note: allocates a zero byte after the end of the file
 * (the checksum code demands this)
 */
static char *loadfile(const char *path, size_t *len_ret) {
  int fd;
  char *buf;
  off_t length;
  ssize_t nread;

  fd = open(path, O_BINARY|O_RDONLY);
  if (fd < 0) {
    die("%s: %s", path, strerror(errno));
  }

  length = filelength(fd);
  if (length > 1000000000L) {
    // paranoia is good
    die("Unreasonably large file");
  }

  buf = malloc(length+1);
  if (!buf) {
    die("malloc failed");
  }

  nread = read(fd, buf, length);
  if (nread < 0) {
    die("%s: read: %s", path, strerror(errno));
  }
  if (nread != length) {
    die("%s: read: short count", path);
  }

  close(fd);

  buf[length] = 0;

  *len_ret = length;
  return buf;
}

static void unloadfile(char *buf) {
  free(buf);
}

////////////////////////////////////////////////////////////

static int search(const char *data, size_t len, size_t *pos_ret) {
  static const char tag[] = TARGETLABEL;
  static unsigned expected = 0;
  static size_t start = 0;

  size_t pos = 0;

  for (pos=0; pos<len; pos++) {
    int ch = data[pos];

    if (ch == tag[expected]) {
      if (expected==0) {
	start = pos;
      }
      expected++;
      if (tag[expected]==0) {
	/* found */
	*pos_ret = start;
	return 1;
      }
    }
    else {
      expected = 0;
    }
  }

  return 0;
}

////////////////////////////////////////////////////////////

static void sumfile(const char *sym, const char *path) {
  char *data;
  uint32_t sum;
  size_t length, patchpos;
  int haspatcharea;

  data = loadfile(path, &length);
  haspatcharea = search(data, length, &patchpos);
  sum = dosum(data, length, haspatcharea ? patchpos : 0, TARGETSIZE);
  unloadfile(data);

  if (haspatcharea && patchpos==0) {
    die("%s: patch area is at file offset 0", path);
  }

  fprintf(datfile, "%s=%lu,%lu",
	  sym, (unsigned long) length, (unsigned long) sum);
  if (haspatcharea) {
    fprintf(datfile, "@%lu", (unsigned long) patchpos);
    fprintf(ctlfile, "%s %lu\n", path, (unsigned long) patchpos);
  }
  else {
    fprintf(ctlfile, "# %s no-patch-area\n", path);
  }
  fprintf(datfile, "\n");
}

////////////////////////////////////////////////////////////

static void usage(void) {
  die("Usage: %s outputprefix symbolic-name pathname", me);
}

int main(int argc, char *argv[]) {
  const char *out, *sym, *path;

  setme(argv[0]);
  if (argc != 4) {
    usage();
  }

  out = argv[1];
  sym = argv[2];
  path = argv[3];

  openoutput(out);
  sumfile(sym, path);
  closeoutput();

  return EXIT_SUCCESS;
}