/* macos.c -- operating system dependant mpack code for the Macintosh
 *
 * (C) Copyright 1993 by Christopher J. Newman
 * All Rights Reserved.
 *
 * Permission to use, copy, modify, distribute, and sell this software and its
 * documentation for any purpose is hereby granted without fee, provided that
 * the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation, and that the name of Christopher J. Newman not be used in
 * advertising or publicity pertaining to distribution of the software without
 * specific, written prior permission.  Christopher J. Newman makes no
 * representations about the suitability of this software for any purpose.  It
 * is provided "as is" without express or implied warranty.
 *
 * CHRISTOPHER J. NEWMAN DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT
 * SHALL CHRISTOPHER J. NEWMAN BE LIABLE FOR ANY SPECIAL, INDIRECT OR
 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
 * OF THIS SOFTWARE.
 */

#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <errno.h>
#include "common.h"
#include "macnapp.h"
#include "macmpack.h"

extern char *malloc(), *realloc();

char *xmalloc(unsigned size)
{
    char *ret;

    if (ret = malloc((unsigned) size))
      return ret;

    yell("Virtual memory exhausted");
    exit(1);
}

char *xrealloc (char *ptr, unsigned size)
{
    char *ret;

    /* xrealloc (NULL, size) behaves like xmalloc (size), as in ANSI C */
    if (ret = !ptr ? malloc ((unsigned) size) : realloc (ptr, (unsigned) size))
      return ret;

    yell("Virtual memory exhausted");
    exit(1);
}

char *strsave(char *str)
{
    char *p = xmalloc(strlen(str)+1);
    strcpy(p, str);
    return p;
}

/* save output filename buffer */
static char *output_fname = NULL;

/* TMPL resource for IDna -- ID to name mappings
 */
char TMPL[] = {
	16 , 'D', 'i', 'r', 'e', 'c', 't', 'o',
	'r', 'y', ' ', 'N', 'u', 'm', 'b', 'e',
	'r', 'D', 'W', 'R', 'D'
};

/* Generate a message-id */
char *os_genid()
{
    char *result;
    long tick;
    unsigned long time;

	tick = TickCount();
    result = malloc(64);
    GetDateTime(&time);
    sprintf(result, "%lu.%lu@random-mac", tick, time);
    
    /* make sure tick count is bumped before we return... */
    while (tick == TickCount());
    
    return (result);
}

/* Create and return directory for a message-id
 */
char *os_idtodir(id)
char *id;
{
	static PCstr buf[257];
	static unsigned char mpackdir[] = "\pmpack-tmp";
	PCstr idbuf[257];
	char *fname;
    short wrefnum, vrefnum, resfile, uqid, createflag = 0;
    long dirid, procid;
    OSErr err;
    Handle h;
    ResType type = 'IDna';
    FInfo finfo;

	/* get name of default volume */
	if (GetVol(P(buf), &wrefnum) != noErr) return (NULL);
	procid = 0;
	if (GetWDInfo(wrefnum, &vrefnum, &dirid, &procid) != noErr) return (NULL);
	
	/* try creating tmp dir */
	err = DirCreate(vrefnum, 0, mpackdir, &dirid);
	if (err != noErr && err != dupFNErr) return (NULL);
	CtoPCstrcat(buf, ":");
	PtoPCstrcat(buf, (char *) mpackdir);
	CtoPCstrcat(buf, ":");
	fname = C(buf) + PCstrlen(buf);
	
	/* open the map file */
	strcpy(fname, "map");
	SetPlen(buf);
	CreateResFile(P(buf));
	if ((err = ResError()) == noErr && GetFInfo(P(buf), vrefnum, &finfo) == noErr) {
		finfo.fdType = 'rsrc';
		finfo.fdCreator = 'RSED';
		SetFInfo(P(buf), vrefnum, &finfo);
	}
	if ((resfile = OpenResFile(P(buf))) < 0) return (NULL);
	if (err == noErr && PtrToHand((Ptr) TMPL, &h, sizeof (TMPL)) == noErr) {
		AddResource(h, 'TMPL', 1000, "\pIDna");
	}
	
	/* is there a mapping for the id? */
	CtoPCstrcpy(idbuf, id);
	h = GetNamedResource(type, P(idbuf));
	
	/* no mapping -- create one */
	if (!h) {
		createflag = 1;
		while ((uqid = UniqueID(type)) < 128);
		h = NewHandle(sizeof (short));
		if (h) (**(short **)h) = uqid;
		AddResource(h, type, uqid, P(idbuf));
		if ((err = ResError()) != noErr) {
			CloseResFile(resfile);
			return (NULL);
		}
	} else {
		uqid = ** (short **) h;
	}
	
	/* set directory name & create it */
	sprintf(fname, "%d:", uqid);
	SetPlen(buf);
	err = DirCreate(vrefnum, 0, P(buf), &dirid);
	if (err != noErr && err != dupFNErr) {
		RmveResource(h);
		DisposHandle(h);
		h = NULL;
	}

	/* done with map file */
	CloseResFile(resfile);
	
	return (h ? C(buf) : NULL);
}

/*
 * We are done with the directory returned by os_idtodir()
 * Remove it
 */
os_donewithdir(dir)
char *dir;
{
	PCstr buf[257];
	short uqid, resfile;
	char *fname;
	Handle h;
	
	CtoPCstrcpy(buf, dir);
	HDelete(0, 0, P(buf));
	fname = strchr(C(buf), ':');
	if (fname && (fname = strchr(fname + 1, ':'))) {
		uqid = atoi(fname + 1);
		strcpy(fname, ":map");
		SetPlen(buf);
		if ((resfile = OpenResFile(P(buf))) >= 0) {
			h = GetResource('IDna', uqid);
			if (h) {
				RmveResource(h);
				DisposHandle(h);
			}
			CloseResFile(resfile);
		}
	}
}

/*
 * Create a new file, with suggested filename "fname".
 * "fname" may have come from an insecure source, so clean it up first.
 * It may also be null.
 * "contentType" is passed in for use by systems that have typed filesystems.
 * "binary" is nonzero if the new file should be opened in binary mode.
 */
FILE *os_newtypedfile(fname, contentType, binary)
char *fname;
char *contentType;
int binary;
{
    char *p;
    static int filesuffix=0;
    char buf[128], *descfname=0;
    FILE *outfile = 0;
    extern long _ftype, _fcreator;
    extern Cursor watch;
    Handle h;
    PCstr tstr[257];

    if (!fname) fname = "";
    
    /* Translate ':' to underscore */
    for (p=fname; *p; p++) {
    	if (*p == ':' || !isprint(*p)) *p = '_';
    }
    
    /* chop filename to length */
    if (strlen(fname) > 31) {
		fname += strlen(fname) - 31;
    }

    if (!fname[0]) {
		do {
		    if (outfile) fclose(outfile);
		    sprintf(buf, "part%d", ++filesuffix);
		} while (outfile = fopen(buf, "r"));
		fname = buf;
    }
    else if (outfile = fopen(fname, "r")) {
    	CtoPCstrcpy(tstr, fname);
    	ParamText(P(tstr), NULL, NULL, NULL);
    	SetCursor(&arrow);
    	if (NAalert(replaceALRT) != iReplace) {
			do {
			    fclose(outfile);
			    sprintf(buf, "%s.%d", fname, ++filesuffix);
			 
			} while (outfile = fopen(buf, "r"));
			fname = buf;
		} else {
	    	fclose(outfile);
	    }
		SetCursor(&watch);
    }

	/* set the type */
	CtoPCstrcpy(tstr, contentType);
	h = GetNamedResource('TyCr', P(tstr));
	if (h) {
		_ftype = (*(OSType **)h)[0];
		_fcreator = (*(OSType **)h)[1];
	} else {
		_ftype = '????';
	}

	/* save file */
    outfile = fopen(fname, "wb");
    if (!outfile) {
    	sprintf(buf, "Couldn't open file %s", fname);
    	warn(buf);
    	return (0);
    }

    if (output_fname) free(output_fname);
    output_fname = strsave(fname);

    if (strlen(fname) > sizeof(buf)-6) {
		descfname = xmalloc(strlen(fname)+6);
    }
    else {
		descfname = buf;
    }
    strcpy(descfname, fname);

    if (p = strrchr(descfname, '.')) *p = '\0';

    strcat(descfname, ".desc");
    (void) rename(TEMPFILENAME, descfname);
    if (descfname != buf) free(descfname);
    CtoPstr(descfname);
    _ftype = 'TEXT';
    _fcreator = 'mPAK';

    return outfile;
}

/*
 * Warn user that the MD5 digest of the last file created by os_newtypedfile()
 * did not match that supplied in the Content-MD5: header.
 */
os_warnMD5mismatch()
{
    char *warning;

    warning = xmalloc(strlen(output_fname) + 100);
    sprintf(warning, "%s was corrupted in transit",
	    output_fname);
    warn(warning);
    free(warning);
}

/* bring up an error dialog for a file error
 */
void os_perror(char *str)
{
	extern int errno;
	char *err = strerror(errno), *scan;
	char msg[256];
	short maxflen;
	
	maxflen = 255 - (strlen(err) + 2);
	if (strlen(str) > maxflen) {
		str += strlen(str) - maxflen;
		for (scan = str; *scan && *scan++ != ':';);
		if (*scan) str = scan;
	}
	sprintf(msg, "%s: %s", str, err);
	yell(msg);
}
