mirror of
https://github.com/autc04/Retro68.git
synced 2024-11-23 15:32:26 +00:00
770 lines
14 KiB
C
770 lines
14 KiB
C
/*
|
|
* hfsutils - tools for reading and writing Macintosh HFS volumes
|
|
* Copyright (C) 1996-1998 Robert Leslie
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*
|
|
* $Id: copyin.c,v 1.8 1998/11/02 22:08:25 rob Exp $
|
|
*/
|
|
|
|
# ifdef HAVE_CONFIG_H
|
|
# include "config.h"
|
|
# endif
|
|
|
|
# ifdef HAVE_FCNTL_H
|
|
# include <fcntl.h>
|
|
# else
|
|
int open(const char *, int, ...);
|
|
# endif
|
|
|
|
# ifdef HAVE_UNISTD_H
|
|
# include <unistd.h>
|
|
# else
|
|
int dup(int);
|
|
# endif
|
|
|
|
# include <stdlib.h>
|
|
# include <string.h>
|
|
# include <errno.h>
|
|
|
|
# include "hfs.h"
|
|
# include "data.h"
|
|
# include "copyin.h"
|
|
# include "charset.h"
|
|
# include "binhex.h"
|
|
# include "crc.h"
|
|
|
|
const char *cpi_error = "no error";
|
|
|
|
extern int errno;
|
|
|
|
# define ERROR(code, str) (cpi_error = (str), errno = (code))
|
|
|
|
# define MACB_BLOCKSZ 128
|
|
|
|
# define TEXT_TYPE "TEXT"
|
|
# define TEXT_CREA "UNIX"
|
|
|
|
# define RAW_TYPE "????"
|
|
# define RAW_CREA "UNIX"
|
|
|
|
/* Copy routines =========================================================== */
|
|
|
|
/*
|
|
* NAME: fork->macb()
|
|
* DESCRIPTION: copy a single fork for MacBinary II
|
|
*/
|
|
static
|
|
int fork_macb(int ifile, hfsfile *ofile, unsigned long size)
|
|
{
|
|
char buf[HFS_BLOCKSZ * 4];
|
|
unsigned long chunk, bytes;
|
|
|
|
while (size)
|
|
{
|
|
chunk = (size < sizeof(buf)) ?
|
|
(size + (MACB_BLOCKSZ - 1)) & ~(MACB_BLOCKSZ - 1) : sizeof(buf);
|
|
|
|
bytes = read(ifile, buf, chunk);
|
|
if (bytes == (unsigned long) -1)
|
|
{
|
|
ERROR(errno, "error reading data");
|
|
return -1;
|
|
}
|
|
else if (bytes != chunk)
|
|
{
|
|
ERROR(EIO, "read incomplete chunk");
|
|
return -1;
|
|
}
|
|
|
|
chunk = (size > bytes) ? bytes : size;
|
|
|
|
bytes = hfs_write(ofile, buf, chunk);
|
|
if (bytes == (unsigned long) -1)
|
|
{
|
|
ERROR(errno, hfs_error);
|
|
return -1;
|
|
}
|
|
else if (bytes != chunk)
|
|
{
|
|
ERROR(EIO, "wrote incomplete chunk");
|
|
return -1;
|
|
}
|
|
|
|
size -= chunk;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* NAME: do_macb()
|
|
* DESCRIPTION: perform copy using MacBinary II translation
|
|
*/
|
|
static
|
|
int do_macb(int ifile, hfsfile *ofile,
|
|
unsigned long dsize, unsigned long rsize)
|
|
{
|
|
if (hfs_setfork(ofile, 0) == -1)
|
|
{
|
|
ERROR(errno, hfs_error);
|
|
return -1;
|
|
}
|
|
|
|
if (fork_macb(ifile, ofile, dsize) == -1)
|
|
return -1;
|
|
|
|
if (hfs_setfork(ofile, 1) == -1)
|
|
{
|
|
ERROR(errno, hfs_error);
|
|
return -1;
|
|
}
|
|
|
|
if (fork_macb(ifile, ofile, rsize) == -1)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* NAME: fork->binh()
|
|
* DESCRIPTION: copy a single fork for BinHex
|
|
*/
|
|
static
|
|
int fork_binh(hfsfile *ofile, unsigned long size)
|
|
{
|
|
char buf[HFS_BLOCKSZ * 4];
|
|
long chunk, bytes;
|
|
|
|
while (size)
|
|
{
|
|
chunk = (size > sizeof(buf)) ? sizeof(buf) : size;
|
|
|
|
bytes = bh_read(buf, chunk);
|
|
if (bytes == -1)
|
|
{
|
|
ERROR(errno, bh_error);
|
|
return -1;
|
|
}
|
|
else if (bytes != chunk)
|
|
{
|
|
ERROR(EIO, "read incomplete chunk");
|
|
return -1;
|
|
}
|
|
|
|
bytes = hfs_write(ofile, buf, chunk);
|
|
if (bytes == -1)
|
|
{
|
|
ERROR(errno, hfs_error);
|
|
return -1;
|
|
}
|
|
else if (bytes != chunk)
|
|
{
|
|
ERROR(EIO, "wrote incomplete chunk");
|
|
return -1;
|
|
}
|
|
|
|
size -= chunk;
|
|
}
|
|
|
|
if (bh_readcrc() == -1)
|
|
{
|
|
ERROR(errno, bh_error);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* NAME: do_binh()
|
|
* DESCRIPTION: perform copy using BinHex translation
|
|
*/
|
|
static
|
|
int do_binh(hfsfile *ofile, unsigned long dsize, unsigned long rsize)
|
|
{
|
|
if (hfs_setfork(ofile, 0) == -1)
|
|
{
|
|
ERROR(errno, hfs_error);
|
|
return -1;
|
|
}
|
|
|
|
if (fork_binh(ofile, dsize) == -1)
|
|
return -1;
|
|
|
|
if (hfs_setfork(ofile, 1) == -1)
|
|
{
|
|
ERROR(errno, hfs_error);
|
|
return -1;
|
|
}
|
|
|
|
if (fork_binh(ofile, rsize) == -1)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* NAME: do_text()
|
|
* DESCRIPTION: perform copy using text translation
|
|
*/
|
|
static
|
|
int do_text(int ifile, hfsfile *ofile)
|
|
{
|
|
char buf[HFS_BLOCKSZ * 4], *ptr;
|
|
long chunk, bytes;
|
|
int len;
|
|
|
|
while (1)
|
|
{
|
|
chunk = read(ifile, buf, sizeof(buf));
|
|
if (chunk == -1)
|
|
{
|
|
ERROR(errno, "error reading source file");
|
|
return -1;
|
|
}
|
|
else if (chunk == 0)
|
|
break;
|
|
|
|
for (ptr = buf; ptr < buf + chunk; ++ptr)
|
|
{
|
|
if (*ptr == '\n')
|
|
*ptr = '\r';
|
|
}
|
|
|
|
len = chunk;
|
|
ptr = cs_macroman(buf, &len);
|
|
if (ptr == 0)
|
|
{
|
|
ERROR(ENOMEM, 0);
|
|
return -1;
|
|
}
|
|
|
|
bytes = hfs_write(ofile, ptr, len);
|
|
free(ptr);
|
|
|
|
if (bytes == -1)
|
|
{
|
|
ERROR(errno, hfs_error);
|
|
return -1;
|
|
}
|
|
else if (bytes != len)
|
|
{
|
|
ERROR(EIO, "wrote incomplete chunk");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* NAME: do_raw()
|
|
* DESCRIPTION: perform copy using no translation
|
|
*/
|
|
static
|
|
int do_raw(int ifile, hfsfile *ofile)
|
|
{
|
|
char buf[HFS_BLOCKSZ * 4];
|
|
long chunk, bytes;
|
|
|
|
while (1)
|
|
{
|
|
chunk = read(ifile, buf, sizeof(buf));
|
|
if (chunk == -1)
|
|
{
|
|
ERROR(errno, "error reading source file");
|
|
return -1;
|
|
}
|
|
else if (chunk == 0)
|
|
break;
|
|
|
|
bytes = hfs_write(ofile, buf, chunk);
|
|
if (bytes == -1)
|
|
{
|
|
ERROR(errno, hfs_error);
|
|
return -1;
|
|
}
|
|
else if (bytes != chunk)
|
|
{
|
|
ERROR(EIO, "wrote incomplete chunk");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Utility Routines ======================================================== */
|
|
|
|
/*
|
|
* NAME: opensrc()
|
|
* DESCRIPTION: open the source file; set hint for destination filename
|
|
*/
|
|
static
|
|
int opensrc(const char *srcname, const char **dsthint, const char *ext,
|
|
int binary)
|
|
{
|
|
int fd, len;
|
|
static char name[HFS_MAX_FLEN + 1];
|
|
const char *cptr;
|
|
char *ptr;
|
|
|
|
if (strcmp(srcname, "-") == 0)
|
|
{
|
|
fd = dup(STDIN_FILENO);
|
|
srcname = "";
|
|
}
|
|
else
|
|
fd = open(srcname, O_RDONLY);
|
|
|
|
if (fd == -1)
|
|
{
|
|
ERROR(errno, "error opening source file");
|
|
return -1;
|
|
}
|
|
|
|
cptr = strrchr(srcname, '/');
|
|
if (cptr == 0)
|
|
cptr = srcname;
|
|
else
|
|
++cptr;
|
|
|
|
if (ext == 0)
|
|
len = strlen(cptr);
|
|
else
|
|
{
|
|
ext = strstr(cptr, ext);
|
|
if (ext == 0)
|
|
len = strlen(cptr);
|
|
else
|
|
len = ext - cptr;
|
|
}
|
|
|
|
if (len > HFS_MAX_FLEN)
|
|
len = HFS_MAX_FLEN;
|
|
|
|
memcpy(name, cptr, len);
|
|
name[len] = 0;
|
|
|
|
for (ptr = name; *ptr; ++ptr)
|
|
{
|
|
switch (*ptr)
|
|
{
|
|
case ':':
|
|
*ptr = '-';
|
|
break;
|
|
|
|
case '_':
|
|
*ptr = ' ';
|
|
break;
|
|
}
|
|
}
|
|
|
|
*dsthint = name;
|
|
|
|
return fd;
|
|
}
|
|
|
|
/*
|
|
* NAME: opendst()
|
|
* DESCRIPTION: open the destination file
|
|
*/
|
|
static
|
|
hfsfile *opendst(hfsvol *vol, const char *dstname, const char *hint,
|
|
const char *type, const char *creator)
|
|
{
|
|
hfsdirent ent;
|
|
hfsfile *file;
|
|
unsigned long cwd;
|
|
|
|
if (hfs_stat(vol, dstname, &ent) != -1 &&
|
|
(ent.flags & HFS_ISDIR))
|
|
{
|
|
cwd = hfs_getcwd(vol);
|
|
|
|
if (hfs_setcwd(vol, ent.cnid) == -1)
|
|
{
|
|
ERROR(errno, hfs_error);
|
|
return 0;
|
|
}
|
|
|
|
dstname = hint;
|
|
}
|
|
|
|
hfs_delete(vol, dstname);
|
|
|
|
file = hfs_create(vol, dstname, type, creator);
|
|
if (file == 0)
|
|
{
|
|
ERROR(errno, hfs_error);
|
|
|
|
if (dstname == hint)
|
|
hfs_setcwd(vol, cwd);
|
|
|
|
return 0;
|
|
}
|
|
|
|
if (dstname == hint)
|
|
{
|
|
if (hfs_setcwd(vol, cwd) == -1)
|
|
{
|
|
ERROR(errno, hfs_error);
|
|
|
|
hfs_close(file);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return file;
|
|
}
|
|
|
|
/*
|
|
* NAME: closefiles()
|
|
* DESCRIPTION: close source and destination files
|
|
*/
|
|
static
|
|
void closefiles(int ifile, hfsfile *ofile, int *result)
|
|
{
|
|
if (ofile && hfs_close(ofile) == -1 && *result == 0)
|
|
{
|
|
ERROR(errno, hfs_error);
|
|
*result = -1;
|
|
}
|
|
|
|
if (close(ifile) == -1 && *result == 0)
|
|
{
|
|
ERROR(errno, "error closing source file");
|
|
*result = -1;
|
|
}
|
|
}
|
|
|
|
/* Interface Routines ====================================================== */
|
|
|
|
/*
|
|
* NAME: cpi->macb()
|
|
* DESCRIPTION: copy a UNIX file to an HFS file using MacBinary II translation
|
|
*/
|
|
int cpi_macb(const char *srcname, hfsvol *vol, const char *dstname)
|
|
{
|
|
int ifile, result = 0;
|
|
hfsfile *ofile;
|
|
hfsdirent ent;
|
|
const char *dsthint;
|
|
char type[5], creator[5];
|
|
unsigned char buf[MACB_BLOCKSZ];
|
|
unsigned short crc;
|
|
unsigned long dsize, rsize;
|
|
|
|
ifile = opensrc(srcname, &dsthint, ".bin", 1);
|
|
if (ifile == -1)
|
|
return -1;
|
|
|
|
if (read(ifile, buf, MACB_BLOCKSZ) < MACB_BLOCKSZ)
|
|
{
|
|
ERROR(errno, "error reading MacBinary file header");
|
|
|
|
close(ifile);
|
|
return -1;
|
|
}
|
|
|
|
if (buf[0] != 0 || buf[74] != 0)
|
|
{
|
|
ERROR(EINVAL, "invalid MacBinary file header");
|
|
|
|
close(ifile);
|
|
return -1;
|
|
}
|
|
|
|
crc = d_getuw(&buf[124]);
|
|
|
|
if (crc_macb(buf, 124, 0x0000) != crc)
|
|
{
|
|
/* (buf[82] == 0) => MacBinary I? */
|
|
|
|
ERROR(EINVAL, "unknown, unsupported, or corrupt MacBinary file");
|
|
|
|
close(ifile);
|
|
return -1;
|
|
}
|
|
|
|
if (buf[123] > 129)
|
|
{
|
|
ERROR(EINVAL, "unsupported MacBinary file version");
|
|
|
|
close(ifile);
|
|
return -1;
|
|
}
|
|
|
|
if (buf[1] < 1 || buf[1] > 63 ||
|
|
buf[2 + buf[1]] != 0)
|
|
{
|
|
ERROR(EINVAL, "invalid MacBinary file header (bad file name)");
|
|
|
|
close(ifile);
|
|
return -1;
|
|
}
|
|
|
|
dsize = d_getul(&buf[83]);
|
|
rsize = d_getul(&buf[87]);
|
|
|
|
if (dsize > 0x7fffffff || rsize > 0x7fffffff)
|
|
{
|
|
ERROR(EINVAL, "invalid MacBinary file header (bad file length)");
|
|
|
|
close(ifile);
|
|
return -1;
|
|
}
|
|
|
|
dsthint = (char *) &buf[2];
|
|
|
|
memcpy(type, &buf[65], 4);
|
|
memcpy(creator, &buf[69], 4);
|
|
type[4] = creator[4] = 0;
|
|
|
|
ofile = opendst(vol, dstname, dsthint, type, creator);
|
|
if (ofile == 0)
|
|
{
|
|
close(ifile);
|
|
return -1;
|
|
}
|
|
|
|
result = do_macb(ifile, ofile, dsize, rsize);
|
|
|
|
if (result == 0 && hfs_fstat(ofile, &ent) == -1)
|
|
{
|
|
ERROR(errno, hfs_error);
|
|
result = -1;
|
|
}
|
|
|
|
ent.fdflags = (buf[73] << 8 | buf[101]) &
|
|
~(HFS_FNDR_ISONDESK | HFS_FNDR_HASBEENINITED | HFS_FNDR_RESERVED);
|
|
|
|
ent.crdate = d_ltime(d_getul(&buf[91]));
|
|
ent.mddate = d_ltime(d_getul(&buf[95]));
|
|
|
|
if (result == 0 && hfs_fsetattr(ofile, &ent) == -1)
|
|
{
|
|
ERROR(errno, hfs_error);
|
|
result = -1;
|
|
}
|
|
|
|
closefiles(ifile, ofile, &result);
|
|
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* NAME: binhx()
|
|
* DESCRIPTION: auxiliary BinHex routine
|
|
*/
|
|
static
|
|
int binhx(char *fname, char *type, char *creator, short *fdflags,
|
|
unsigned long *dsize, unsigned long *rsize)
|
|
{
|
|
int len;
|
|
unsigned char byte, word[2], lword[4];
|
|
|
|
if (bh_read(&byte, 1) < 1)
|
|
{
|
|
ERROR(errno, bh_error);
|
|
return -1;
|
|
}
|
|
|
|
len = (unsigned char) byte;
|
|
|
|
if (len < 1 || len > HFS_MAX_FLEN)
|
|
{
|
|
ERROR(EINVAL, "invalid BinHex file header (bad file name)");
|
|
return -1;
|
|
}
|
|
|
|
if (bh_read(fname, len + 1) < len + 1)
|
|
{
|
|
ERROR(errno, bh_error);
|
|
return -1;
|
|
}
|
|
|
|
if (fname[len] != 0)
|
|
{
|
|
ERROR(EINVAL, "invalid BinHex file header (bad file name)");
|
|
return -1;
|
|
}
|
|
|
|
if (bh_read(type, 4) < 4 ||
|
|
bh_read(creator, 4) < 4 ||
|
|
bh_read(word, 2) < 2)
|
|
{
|
|
ERROR(errno, bh_error);
|
|
return -1;
|
|
}
|
|
*fdflags = d_getsw(word);
|
|
|
|
if (bh_read(lword, 4) < 4)
|
|
{
|
|
ERROR(errno, bh_error);
|
|
return -1;
|
|
}
|
|
*dsize = d_getul(lword);
|
|
|
|
if (bh_read(lword, 4) < 4)
|
|
{
|
|
ERROR(errno, bh_error);
|
|
return -1;
|
|
}
|
|
*rsize = d_getul(lword);
|
|
|
|
if (*dsize > 0x7fffffff || *rsize > 0x7fffffff)
|
|
{
|
|
ERROR(EINVAL, "invalid BinHex file header (bad file length)");
|
|
return -1;
|
|
}
|
|
|
|
if (bh_readcrc() == -1)
|
|
{
|
|
ERROR(errno, bh_error);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* NAME: cpi->binh()
|
|
* DESCRIPTION: copy a UNIX file to an HFS file using BinHex translation
|
|
*/
|
|
int cpi_binh(const char *srcname, hfsvol *vol, const char *dstname)
|
|
{
|
|
int ifile, result;
|
|
hfsfile *ofile;
|
|
hfsdirent ent;
|
|
const char *dsthint;
|
|
char fname[HFS_MAX_FLEN + 1], type[5], creator[5];
|
|
short fdflags;
|
|
unsigned long dsize, rsize;
|
|
|
|
ifile = opensrc(srcname, &dsthint, ".hqx", 0);
|
|
if (ifile == -1)
|
|
return -1;
|
|
|
|
if (bh_open(ifile) == -1)
|
|
{
|
|
ERROR(errno, bh_error);
|
|
|
|
close(ifile);
|
|
return -1;
|
|
}
|
|
|
|
if (binhx(fname, type, creator, &fdflags, &dsize, &rsize) == -1)
|
|
{
|
|
bh_close();
|
|
close(ifile);
|
|
return -1;
|
|
}
|
|
|
|
dsthint = fname;
|
|
|
|
ofile = opendst(vol, dstname, dsthint, type, creator);
|
|
if (ofile == 0)
|
|
{
|
|
bh_close();
|
|
close(ifile);
|
|
return -1;
|
|
}
|
|
|
|
result = do_binh(ofile, dsize, rsize);
|
|
|
|
if (bh_close() == -1 && result == 0)
|
|
{
|
|
ERROR(errno, bh_error);
|
|
result = -1;
|
|
}
|
|
|
|
if (result == 0 && hfs_fstat(ofile, &ent) == -1)
|
|
{
|
|
ERROR(errno, hfs_error);
|
|
result = -1;
|
|
}
|
|
|
|
ent.fdflags = fdflags &
|
|
~(HFS_FNDR_ISONDESK | HFS_FNDR_HASBEENINITED | HFS_FNDR_ISINVISIBLE);
|
|
|
|
if (result == 0 && hfs_fsetattr(ofile, &ent) == -1)
|
|
{
|
|
ERROR(errno, hfs_error);
|
|
result = -1;
|
|
}
|
|
|
|
closefiles(ifile, ofile, &result);
|
|
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* NAME: cpi->text()
|
|
* DESCRIPTION: copy a UNIX file to an HFS file using text translation
|
|
*/
|
|
int cpi_text(const char *srcname, hfsvol *vol, const char *dstname)
|
|
{
|
|
int ifile, result = 0;
|
|
hfsfile *ofile;
|
|
const char *dsthint;
|
|
|
|
ifile = opensrc(srcname, &dsthint, ".txt", 0);
|
|
if (ifile == -1)
|
|
return -1;
|
|
|
|
ofile = opendst(vol, dstname, dsthint, TEXT_TYPE, TEXT_CREA);
|
|
if (ofile == 0)
|
|
{
|
|
close(ifile);
|
|
return -1;
|
|
}
|
|
|
|
result = do_text(ifile, ofile);
|
|
|
|
closefiles(ifile, ofile, &result);
|
|
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* NAME: cpi->raw()
|
|
* DESCRIPTION: copy a UNIX file to the data fork of an HFS file
|
|
*/
|
|
int cpi_raw(const char *srcname, hfsvol *vol, const char *dstname)
|
|
{
|
|
int ifile, result = 0;
|
|
hfsfile *ofile;
|
|
const char *dsthint;
|
|
|
|
ifile = opensrc(srcname, &dsthint, 0, 1);
|
|
if (ifile == -1)
|
|
return -1;
|
|
|
|
ofile = opendst(vol, dstname, dsthint, RAW_TYPE, RAW_CREA);
|
|
if (ofile == 0)
|
|
{
|
|
close(ifile);
|
|
return -1;
|
|
}
|
|
|
|
result = do_raw(ifile, ofile);
|
|
|
|
closefiles(ifile, ofile, &result);
|
|
|
|
return result;
|
|
}
|