[comp.protocols.appletalk] MacBinary-to-CAP file converter

dplatt@coherent.com (Dave Platt) (07/27/90)

Well, I finally decided to get off of my rear end and write a utility
which could convert a MacBinary file into CAP/Aufs format.  By cutting
and splicing portions of "cvt2cap", Aufs code, and the "macbin" program,
I managed to get something which seems to work reasonably well (on my
Sparcstation, at least).  

It lacks somewhat in its error checking capability (it doesn't verify
the CRC in the MacBinary II header);  it assumes MacBinary II format
(which is mostly what one finds these days);  and the source-code
contains a fair bit of residue from its origin as part of three
different programs, and could use some cleanup.  Quite frankly, it's a
quick and somewhat ugly hack.

Nevertheless, it works.  I'm sure I'm not the only CAP user who has been
wishing for a utility of this sort... so, here it is.  It's small, so
I'm posting it here rather than going through comp.sources.whatever...
may the Net Gods forgive me for my presumption.

/*
 *	bin2cap
 *
 *	This program converts MacBinary files to CAP/aufs
 *	format files.
 *
 *		bin2cap [sourcefile]
 *
 *      The specified source file (or the standard input if no source
 *      file is specified) is read, verified to be in MacBinary format,
 *      and used to create an Aufs-compatible file in the current working
 *      directory.  The original file is left unchanged.
 *
 *	COPYRIGHT NOTICE
 *
 *      Copyright 1990 by Dave Platt.  All Rights Reserved.
 *
 *	Permission is granted to any individual or institution to use, copy,
 *	or redistribute this software so long as it is not sold for profit,
 *	provided that this notice and the original copyright notices are
 *	retained.  Dave Platt makes no representations about the
 *	suitability of this software for any purpose.  It is provided "as is"
 *	without express or implied warranty.
 *      
 *      Portions of this code are based on the "cvt2cap" program by Paul
 *      Campbell;  on the Aufs file-server package by Charlie Kim et al;  and
 *      on the "macbin" program (author unknown to me).  Relevant copyrights
 *      are included herein.
 *
 *      For portions derived from Aufs:
 *
 *      Copyright (c) 1986, 1987, 1988 by The Trustees of Columbia University
 *      in the City of New York.
 *
 *      For portions derived from cvt2cap:
 *	
 *	Copyright (c) May 1988, Paul Campbell, All Rights Reserved.
 *	
 *	Permission is granted to any individual or institution to use, copy,
 *	or redistribute this software so long as it is not sold for profit,
 *	provided that this notice and the original copyright notices are
 *	retained.  Paul Campbell makes no representations about the
 *	suitability of this software for any purpose.  It is provided "as is"
 *	without express or implied warranty.
 *	
 *  History:
 *    4/23/88 Paul Campbell, submitted to CAP distribution
 *    4/23/88 Charlie C. Kim, clean up and modify to work with
 *            byte swapped machines
 *    7/26/90 Dave Platt, beaten about the head and shoulders with a stick
 *            until it would eat MacBinary files rather than AppleSingle
 *            or AppleDouble.
 *
 */	


#include <stdio.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <netat/appletalk.h>
#include <netat/macfile.h>

char *prog;

struct macbinhdr {
/* 000 */  char zero1;
/* 001 */  char nlen;
/* 002 */  char name[63];
/* 065 */  char type[4];
/* 069 */  char creator[4];
/* 073 */  char flags;	
/* 074 */  char zero2;	
/* 075 */  char vert[2];
/* 077 */  char horiz[2];
/* 079 */  char folderID[2];
/* 081 */  char protected;		
/* 082 */  char zero3;		
/* 083 */  char dflen[4];
/* 087 */  char rflen[4];
/* 091 */  char cdate[4];
/* 095 */  char mdate[4];
/* 099 */  char ilen[2];
/* 101 */  char flags2;
/* 102 */  char zero4[20];
/* 122 */  char mbvers;
/* 123 */  char minvers;
/* 124 */  char crc[2];
/* 126 */  char zero5[2];	   
} header;

	
char dir[] = "";
char file[1025];
char ename[1024],iname[1024];

static char *hexdigits = "0123456789abcdef";

FileInfo finfo;
fileFinderInfo *finderInfo;

FILE *fiopen();
void EtoIName();
int ENameLen();

main(argc, argv)
char **argv;
{
	char *fin, *fout;
	FILE *f, *fd, *fx;
	register int i, j;
	int resource, data;
	u_long temp;

	bzero(&finfo, sizeof(finfo)); /* make sure clear first */

	prog = argv[0];

	if (argc > 2)
	  {
	    usage();
	  }

	if (argc == 1)
	  {
	    f = stdin;
	  }
	else
	  {
	    if ((f = fiopen(dir, NULL, argv[1], "r")) == NULL)
	      {
		error("can't open input file %", argv[1]);
	      }
	  }


	/*
	 *	Read the header 
	 */

	if (fread(&header, sizeof header, 1, f) < 1)
	  {
	    error("could not read MacBinary header header");
	  }

	if (header.zero1 != 0 || header.zero2 != 0 || header.nlen <= 0 ||
	    header.nlen > 63)
	  {
	    error ("not a MacBinary file!");
	  }

	bzero(ename, sizeof ename);
	strncpy(ename, header.name, header.nlen);
	if (ENameLen(ename) > 31)
	  {
	    error("encoded name > 31 characters");
	  }
	EtoIName(ename, iname);

	finderInfo = (fileFinderInfo *) finfo.fi_fndr;
	bcopy(header.type, finderInfo->fdType, 4);
	bcopy(header.creator, finderInfo->fdCreator, 4);
	finderInfo->fdFlags = ((header.flags << 8) | header.flags2) & 0xf8fc;

	finfo.fi_magic1 = FI_MAGIC1;
	finfo.fi_version = FI_VERSION;
	finfo.fi_magic = FI_MAGIC;
	finfo.fi_bitmap = FI_BM_MACINTOSHFILENAME;
	finfo.fi_macfilename[0] = header.nlen;
	bcopy(header.name, finfo.fi_macfilename+1, header.nlen);

	fx = fiopen(dir, ".finderinfo/", iname, "w");
	if (fx == NULL)
		error("cannot create output finder info file '%s'", iname);
	if (fwrite(&finfo, sizeof(finfo), 1, fx) < 1)
		error("cannot write output finder info file '%s'", iname);
	fclose(fx);

	bcopy(header.dflen, &temp, sizeof temp);
	data = ntohl(temp);

	bcopy(header.rflen, &temp, sizeof temp);
	resource = ntohl(temp);

	fx = fiopen(dir, NULL, iname, "w");
	if (fx == NULL)
	  error("cannot create output data file '%s'", iname);
	fcopy(fx, f, data);
	fclose(fx);

	if (resource > 0)
	  {
	    while ((data%128) != 0)
	      {
		(void) fgetc(f);
		data++;
	      }

	    fx = fiopen(dir, ".resource/", iname, "w");
	    if (fx == NULL)
	      error("cannot create output resource file '%s'",
		    iname);
	    fcopy(fx, f, resource);
	    fclose(fx);
	  }
}

/*
 *	open the file "dir""ext""file" with mode "mode"
 */

FILE *
fiopen(dir, ext, file, mode)
char *dir, *ext, *file, *mode;
{
	char name[1025];

	strcpy(name, dir);
	if (ext)
		strcat(name, ext);
	strcat(name, file);
	return(fopen(name, mode));
}

/*
 *	print a nasty message
 */

usage()
{
	fprintf(stderr, "Usage: %s [macbinary-file]\n",
			prog);
	exit(1);
}

/*
 *	copy length bytes from fin to fout
 */

fcopy(fout, fin, length)
FILE *fin, *fout;
unsigned length;
{
	char buffer[4096];
	register unsigned l;

	while (length > 0) {
	  l = sizeof buffer;
	  if (l > length)
	    l = length;
	  l = fread(buffer, 1, l, fin);
	  if (l > 0) {
	    if (fwrite(buffer, 1, l, fout) != l)
	      error("error writing output file");
	  } else break;
	  length -= l;
	}
}

/*
 * 	print another nasty message and quit 
 */

error(s, a, b, c, d, e, f)
char *s;
{
	fprintf(stderr, "%s: ", prog);
	fprintf(stderr, s, a, b, c, d, e, f);
	fprintf(stderr, "\n");
	exit(2);
}

void
EtoIName(en,inp)
register byte *en;		/* max is 31 or so */
char *inp;
{
  byte c;			/* unsigned char */
  register char *in = inp;
  register int cnt = 0;

  while ((c = *en++) != '\0') {
    if (isascii(c) && !iscntrl(c) && isprint(c) && c != '/') {
      *in++ = c;
      cnt++;
    } else {
      /* must convert to */
      *in++ = ':';			/* : */
      *in++ = hexdigits[(c >> 4) & 0xf];
      *in++ = hexdigits[(c & 0xf)];
      cnt += 3;
    }
  }
  *in++ = '\0';
}

/*
 * Given an internal file name, compute the length of the external
 * file name
 */
int
ENameLen(in)
register char *in;
{
  register int len = 0;
  register char c;
  register char c2;

  while ((c = *in++) != '\0') {
    if (c != ':')
      len++;
    else {
      /* must convert to external form */
      if ((c = *in++) == '\0' || (c2 = *in++) == '\0') {
	len++;
	if (c != '\0')
	  len++;
	break;				/* done with while */
      }
      if (index(hexdigits,c) == NULL || index(hexdigits,c2) == NULL) 
	len += 3;
      else
	len++;
    }
  }	
  return(len);
}