1
0
mirror of https://github.com/PDP-10/klh10.git synced 2026-03-09 04:09:38 +00:00
Files
PDP-10.klh10/src/vdkfmt.c

762 lines
18 KiB
C
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* VDKFMT.C - Utility to copy or format virtual disks.
*/
/* $Id: vdkfmt.c,v 2.6 2002/05/21 09:51:26 klh Exp $
*/
/* Copyright © 1994, 2001 Kenneth L. Harrenstien
** All Rights Reserved
**
** This file is part of the KLH10 Distribution. Use, modification, and
** re-distribution is permitted subject to the terms in the file
** named "LICENSE", which contains the full text of the legal notices
** and should always accompany this Distribution.
**
** This software is provided "AS IS" with NO WARRANTY OF ANY KIND.
**
** This notice (including the copyright and warranty disclaimer)
** must be included in all copies or derivations of this software.
*/
/*
* $Log: vdkfmt.c,v $
* Revision 2.6 2002/05/21 09:51:26 klh
* Change protos for vdk_read, vdk_write
*
* Revision 2.5 2001/11/19 10:51:43 klh
* Bugfix: was freeing d_path which is now static.
*
* Revision 2.4 2001/11/10 21:28:59 klh
* Final 2.0 distribution checkin
*
*/
/* VDKFMT is mainly used to copy or format virtual disks.
** later it may become a general-purpose utility for managing
** KLH10 disk packs.
**
*/
#include <string.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h> /* exit() */
#include <stdarg.h>
#include <errno.h>
#include <sys/file.h> /* For open() flags */
#include "rcsid.h"
#include "cenv.h"
#include "osdsup.h"
#include "vdisk.h" /* Include virtual disk stuff */
#if CENV_SYS_UNIX
# include <unistd.h> /* Basic Unix syscalls */
# include <sys/types.h>
# include <sys/ioctl.h>
# define NULLDEV "/dev/null"
# define FD_STDIN 0
# define FD_STDOUT 1
#endif
#if HAVE_ERRNO_H
# include <errno.h>
#endif
#define FNAMSIZ 200
#define TRUE 1
#define FALSE 0
#ifdef RCSID
RCSID(vdkfmt_c,"$Id: vdkfmt.c,v 2.6 2002/05/21 09:51:26 klh Exp $")
#endif
/* Disk type configuration params.
** All of these numbers assume drives using 18-bit formatting.
** (16-bit formatting has more sectors per track)
*/
static struct diskconf {
char dcf_name[8]; /* Drive type name (RP06, etc) */
int dcf_type; /* Type bits for type register */
int dcf_nwds; /* Words/Sector */
int dcf_nsec; /* Sectors/Track */
int dcf_ntrk; /* Tracks/Cylinder */
int dcf_ncyl; /* Cylinders/Drive */
} diskconfs[] = {
/* wrd sec trk cyl Cyl+maint totsec totmaintsec */
{ "RP02", 0, 128, 10, 20, 203 }, /* 200+3 40000 40,600 */
{ "RP03", 0, 128, 10, 20, 403 }, /* 400+3 80000 80,600 */
{ "RP04", 0, 128, 20, 19, 411 }, /* 406+5 154280 156,180 */
{ "RP05", 0, 128, 20, 19, 411 }, /* " " " */
{ "RP06", 0, 128, 20, 19, 815 }, /* 810+5 307800 309,700 */
{ "RP07", 0, 128, 43, 32, 630 }, /* 629+1 865504 866,880 */
{ "RM03", 0, 128, 30, 5, 823 }, /* 821+2 123150 123,450 */
{ "RM02", 0, 128, 30, 5, 823 }, /* " " " */
{ "RM05", 0, 128, 30, 19, 823 }, /* 821+2 467970 469,110 */
{ "RM80", 0, 128, 30, 14, 561 }, /* 559+2 234780 235,620 */
{ "" }
};
/* RP07 doc p.6-23 says addressing params are:
** Mode Cyl Head/Track Sector(18) Sector(16)
** Functional: 630 32 43 50
** Diagnostic: 632 32 43 50
*/
/* In ITS:
RP06 was 812+3
RP07 was 627+3
RM03 was 821+2
RM05 was 820
RM80 was 556+3
*/
/* VDKFMT parameters */
char nusage[] = "\
Usage: vdkfmt <params>\n\
ip=<path> Input disk device/file:\n\
op=<path> Output disk device\n\
ifmt=<fmt> format of input pack data\n\
ofmt=<fmt> format of output pack data\n\
dt=<type> Type of drive (RP06, etc)\n\
log=<path> Log filespec (optional, defaults to stderr)\n\
verbose Verbose (optional)\n\
";
/* Switch parameters */
int sw_tdtest;
int sw_peot;
int sw_verbose;
int sw_maxsec;
int sw_maxfile;
char *sw_logpath;
FILE *logfile;
#define DBGFLG sw_verbose
struct devdk {
char *d_pname; /* Print name, "In" or "Out" */
char *d_path; /* Disk drive path spec */
int d_isdisk; /* NZ if hardware device, else virtual */
int d_fmt; /* Format to use */
long d_totsec; /* Total # sectors */
struct vdk_unit d_vdk; /* Virtual disk info */
struct diskconf d_dcf;
};
struct devdk dvi = { "In" };
struct devdk dvo = { "Out" };
/* Values for d_isdisk */
#define MTYP_NULL 0 /* Null device */
#define MTYP_VIRT 1 /* Virtual disk */
#define MTYP_HARD 2 /* Hard raw disk partition */
char *mtypstr[] = {
"nulldev",
"virtual",
"hard"
};
int cmdsget(int ac, char **av);
int docopy(void);
int zerosector(w10_t *wp, int nwds);
int devopen(struct devdk *d, int wrtf);
int devclose(struct devdk *d);
int devread(struct devdk *d, long int daddr, w10_t *buff);
int devwrite(struct devdk *d, long int daddr, w10_t *buff);
void swerror(char *fmt, ...);
void efatal(char *errmsg);
void errhan(struct vdk_unit *t, char *s);
/* For now, must include VDISK source directly, so as to avoid compile-time
** switch conflicts (eg with KLH10).
*/
#include "vdisk.c"
static int partyp(char *cp, struct diskconf *dcf)
{
register struct diskconf *dc;
for (dc = diskconfs; dc->dcf_name[0]; ++dc) {
if (strcasecmp(cp, dc->dcf_name) == 0) {
*dcf = *dc; /* Found match, won */
return TRUE;
}
}
return FALSE; /* Unknown disk type */
}
/* Parse disk format -- something that VDISK understands.
*/
static char *fmttab[] = {
# define vdk_fmt(i,n,c,s,f,t) n
VDK_FORMATS
# undef vdk_fmt
};
static int parfmt(char *cp, int *afmt)
{
register int i;
for (i = 0; i < VDK_FMT_N; ++i) {
if (strcasecmp(cp, fmttab[i]) == 0) {
*afmt = i; /* Found match, won */
return TRUE;
}
}
return FALSE; /* Unknown disk format */
}
/* Error handling */
/* Copied from OSDSUP.C */
char *
os_strerror(int err)
{
if (err == -1 && errno != err)
return os_strerror(errno);
#if HAVE_STRERROR
return strerror(err);
#else
# if HAVE_SYS_ERRLIST
{
# if DECL_SYS_ERRLIST
extern int sys_nerr;
extern char *sys_errlist[];
# endif
if (0 < err && err <= sys_nerr)
return sys_errlist[err];
}
# endif /* HAVE_SYS_ERRLIST */
if (err == 0)
return "No error";
else {
static char ebuf[30];
sprintf(ebuf, "Unknown-error-%d", err);
return ebuf;
}
#endif /* !HAVE_STRERROR */
}
void errhan(struct vdk_unit *t, char *s)
{
fprintf(logfile, "; %s: %s\n", t->dk_devname, s);
}
void efatal(char *errmsg) /* print error message and exit */
/* error message string */
{
fflush(stdout);
fprintf(logfile, "\n?%s\n",errmsg);
exit(1);
}
int
main(int argc, char **argv)
{
int ret;
logfile = stderr;
signal(SIGINT, exit); /* Allow int to terminate log files etc */
dvi.d_fmt = dvo.d_fmt = -1;
if ((ret = cmdsget(argc, argv))) /* Parse and handle command line */
exit(ret);
if (!sw_logpath)
logfile = stderr;
else {
if ((logfile = fopen(sw_logpath, "w")) == NULL) {
logfile = stderr;
fprintf(logfile, "; Cannot open log file \"%s\", using stderr.\n",
sw_logpath);
}
}
/* Set up defaults for devices, and log all params if requested */
dvi.d_totsec = dvo.d_totsec =
dvi.d_dcf.dcf_nsec *
dvi.d_dcf.dcf_ntrk *
dvi.d_dcf.dcf_ncyl;
if (sw_verbose) {
fprintf(logfile, "; Input disk spec \"%s\" (Type: %s) Format: %s\n",
dvi.d_path, mtypstr[dvi.d_isdisk],
fmttab[dvi.d_fmt]);
fprintf(logfile, "; Output disk spec \"%s\" (Type: %s) Format: %s\n",
dvo.d_path, mtypstr[dvo.d_isdisk],
fmttab[dvo.d_fmt]);
/* Show config info here */
fprintf(logfile, "; Drive type: %s\n", dvi.d_dcf.dcf_name);
fprintf(logfile, "; %ld sectors, %d wds/sec\n", dvi.d_totsec,
dvi.d_dcf.dcf_nwds);
fprintf(logfile, "; (%d secs, %d trks, %d cyls\n",
dvi.d_dcf.dcf_nsec,
dvi.d_dcf.dcf_ntrk,
dvi.d_dcf.dcf_ncyl);
if (sw_logpath)
fprintf(logfile, "; Using logging path %s\n", sw_logpath);
}
/* Open I/O files as appropriate */
if (!devopen(&dvi, FALSE)) /* Open for reading */
exit(1);
if (!devopen(&dvo, TRUE)) /* Open for writing */
exit(1);
/* Do it! */
fprintf(logfile, "; Copying from \"%s\" to \"%s\"...\n", dvi.d_path,
dvo.d_path ? dvo.d_path : NULLDEV);
if ((ret = docopy()))
ret = devclose(&dvo);
else (void) devclose(&dvo);
if (!ret) fprintf(logfile, "; Stopped unexpectedly.\n");
#if 0
{
register struct devdk *d;
for (d = &dvi; d; d = (d == &dvi) ? &dvo : NULL) {
if (d->d_isdisk)
fprintf(logfile, "; %s: %d+%d errs, %d secs, %ld bytes\n",
d->d_pname, d->mta_herr, d->mta_serr,
d->d_secs, d->d_tloc);
}
}
#endif
fclose(logfile);
exit(ret ? 0 : 1);
}
static char pagsym[4] = { '.', '-', '=', '#'};
int docopy(void)
{
int err;
long nsect = 0;
w10_t wbuff[512];
int nwrt = 0;
if (DBGFLG)
fprintf(logfile, "; Pages:\n");
for (; nsect <= dvi.d_totsec;) {
err = devread(&dvi, nsect, wbuff); /* Get a sector */
if (!err) {
fprintf(logfile, "; Aborting loop, last err: %s\n", os_strerror(-1));
return 0;
}
/* See whether there's any data in sector or not.
** If none, don't write it out!
** Later, always write if device is "hard".
*/
if (!zerosector(wbuff, 128)) {
/* Copy results to output device */
nwrt++;
err = devwrite(&dvo, nsect, wbuff); /* Write a sector */
if (!err) {
fprintf(logfile, "; Aborting loop, last err: %s\n",
os_strerror(-1));
return 0;
}
}
++nsect;
/* Hack to show nice pattern, one char per 4-sector page */
if (DBGFLG) {
if ((nsect & 03) == 0) {
putc(pagsym[nwrt&03], logfile);
nwrt = 0;
}
}
}
if (DBGFLG)
fprintf(logfile, "\n");
return TRUE;
}
int zerosector(register w10_t *wp, register int nwds)
{
for (; --nwds >= 0; ++wp)
if (LHGET(*wp) || RHGET(*wp))
return FALSE;
return TRUE;
}
int swerrs = 0;
void swerror(char *fmt, ...)
{
++swerrs;
{
va_list ap;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
}
putc('\n', stderr);
}
int
cmdsget(int ac, char **av)
{
register char *cp, *arg;
register int plen;
struct devdk *d;
dvi.d_isdisk = dvo.d_isdisk = MTYP_NULL;
while (--ac > 0 && (cp = *++av)) {
if ((arg = strchr(cp, '='))) /* If arg furnished for param, */
*arg++ = '\0'; /* split arg off */
if ((plen = strlen(cp)) <= 0)
break; /* Bad param */
/* Now identify parameter. No case folding for now, sigh */
if (*cp == 'i' || *cp == 'o') {
/* Handle {i,o}{t,d,f,b} */
if (!arg) {
swerror("Parameter requires arg: \"%s\"", *av);
continue;
}
d = (*cp == 'i') ? &dvi : &dvo;
switch (*++cp) {
case 'p':
if (strlen(cp) > 2) break;
if (d->d_path) {
swerror("Param already specified: \"%s\"", *av);
continue;
}
d->d_path = arg;
switch (*++cp) {
case '\0':
case 'v': d->d_isdisk = MTYP_VIRT; continue;
case 'h': d->d_isdisk = MTYP_HARD; continue;
/* default falls thru to fail */
}
break;
case 'f':
if (strcmp(cp, "fmt")!=0)
break;
if (d->d_fmt != -1) {
swerror("Param already specified: \"%s\"", *av);
continue;
}
if (!parfmt(arg, &(d->d_fmt))) {
swerror("Unknown format: \"%s\"", arg);
continue;
}
continue;
/* Default just drops thru to fail */
}
swerror("Unknown parameter \"%s\"", *av);
continue;
}
if (strcmp(cp, "drive")==0 || strcmp(cp, "dt")==0) {
if (!partyp(arg, &(dvi.d_dcf)))
swerror("Unknown drive type: \"%s\"", arg ? arg : "");
continue;
} else if (strcmp(cp, "log")==0) {
if (!(sw_logpath = arg))
swerror("Bad arg to log: \"\"");
continue;
} else if (strcmp(cp, "verbose")==0 || strcmp(cp, "v")) {
sw_verbose = TRUE;
continue;
}
swerror("Unknown parameter \"%s\"\n%s", *av, nusage);
return 1;
}
/* Clean up params */
if (dvi.d_dcf.dcf_nwds)
dvo.d_dcf = dvi.d_dcf; /* Copy drive type params */
else
swerror("Drive type must be specified");
if (dvi.d_fmt == -1)
swerror("Input pack format must be specified");
if (dvo.d_fmt == -1)
swerror("Output pack format must be specified");
/* Check for any parameter errors */
if (swerrs) {
fprintf(stderr, "%s", nusage);
return swerrs;
}
return 0;
}
/* Generic device routines (null, virtual, and real)
** Open, Close, Read, Write, Write-EOF, Write-EOT.
*/
int devopen(register struct devdk *d, int wrtf)
{
char *opath, *path;
if (!(opath = d->d_path) || !*opath) {
fprintf(logfile, "; Null mount path\n");
return FALSE;
}
path = malloc(strlen(opath)+1);
strcpy(path, opath);
/* Set up config vars */
vdk_init(&(d->d_vdk), errhan, NULL);
d->d_vdk.dk_format = d->d_fmt;
strcpy(d->d_vdk.dk_devname, d->d_dcf.dcf_name);
d->d_vdk.dk_ncyls = d->d_dcf.dcf_ncyl;
d->d_vdk.dk_ntrks = d->d_dcf.dcf_ntrk;
d->d_vdk.dk_nsecs = d->d_dcf.dcf_nsec;
d->d_vdk.dk_nwds = d->d_dcf.dcf_nwds;
if (!vdk_mount(&(d->d_vdk), path, wrtf)) {
fprintf(logfile, "; Cannot mount device \"%s\": %s\n",
path, os_strerror(d->d_vdk.dk_err));
free(path);
return FALSE;
}
free(path);
return TRUE;
}
int devclose(struct devdk *d)
{
int res = TRUE;
if (DBGFLG && d->d_path)
fprintf(logfile, "; Closing \"%s\"\n", d->d_path);
if (d->d_isdisk) {
res = vdk_unmount(&(d->d_vdk)); /* Close real disk */
}
/* Force us to forget about it even if above stuff failed */
d->d_isdisk = FALSE;
return res;
}
/* Read from device
** Returns 1 if read something
** Returns 0 if read nothing or error
*/
int devread(struct devdk *d, long int daddr, w10_t *buff)
{
int nsec;
#if 0
if (DBGFLG)
fprintf(logfile, "; read daddr=%ld\n", daddr);
#endif
nsec = vdk_read(&d->d_vdk, buff, (uint32)daddr, 1);
if (d->d_vdk.dk_err
|| (nsec != 1)) {
fprintf(logfile, "; read error on %s: %s\n",
d->d_vdk.dk_filename, os_strerror(d->d_vdk.dk_err));
return FALSE;
}
return TRUE;
}
/* Write to device.
*/
int devwrite(struct devdk *d, long int daddr, w10_t *buff)
{
int nsec;
#if 0
if (DBGFLG)
fprintf(logfile, "; write daddr=%ld\n", daddr);
#endif
nsec = vdk_write(&d->d_vdk, buff, (uint32)daddr, 1);
if (d->d_vdk.dk_err
|| (nsec != 1)) {
fprintf(logfile, "; write error on %s: %s\n",
d->d_vdk.dk_filename, os_strerror(d->d_vdk.dk_err));
return FALSE;
}
return TRUE;
}
/* General-purpose System-level I/O.
** It is intended that this level of IO be in some sense the fastest
** or most efficient way to interact with the host OS, as opposed to
** the more portable stdio interface.
*/
int
os_fdopen(osfd_t *afd, char *file, char *modes)
{
#if CENV_SYS_UNIX || CENV_SYS_MAC
int flags = 0;
if (!afd) return FALSE;
for (; modes && *modes; ++modes) switch (*modes) {
case 'r': flags |= O_RDONLY; break; /* Yes I know it's 0 */
case 'w': flags |= O_WRONLY; break;
case '+': flags |= O_RDWR; break;
case 'a': flags |= O_APPEND; break;
case 'b': /* Binary */
# if CENV_SYS_MAC
flags |= O_BINARY;
# endif
break;
case 'c': flags |= O_CREAT; break;
/* Ignore unknown chars for now */
}
# if CENV_SYS_MAC
if ((*afd = open(file, flags)) < 0)
# else
if ((*afd = open(file, flags, 0666)) < 0)
# endif
return FALSE;
return TRUE;
#elif CENV_SYS_MOONMAC
Boolean create = FALSE, append = FALSE;
short refnum;
OSErr err;
char pascal_string[256];
if (!afd) return FALSE;
for (; modes && *modes; ++modes) switch (*modes) {
case 'a': append = TRUE; break;
case 'c': create = TRUE; break;
/* Ignore read/write and unknown chars for now */
}
pascal_string[0] = strlen(file);
BlockMove(file, &pascal_string[1], pascal_string[0]);
if (create) Create(pascal_string, 0, 'KH10', 'TEXT');
err = FSOpen(pascal_string, 0, &refnum);
*afd = err;
if (err) return FALSE;
*afd = refnum;
return TRUE;
#endif
}
int
os_fdclose(osfd_t fd)
{
#if CENV_SYS_UNIX || CENV_SYS_MAC
return close(fd) != -1;
#else
return 0;
#endif
}
int
os_fdseek(osfd_t fd, osdaddr_t addr)
{
#if CENV_SYS_UNIX
return lseek(fd, addr, L_SET) != -1;
#elif CENV_SYS_MAC || CENV_SYS_MOONMAC
return lseek(fd, addr, SEEK_SET) != -1;
#else
return 0;
#endif
}
int
os_fdread(osfd_t fd, char *buf, size_t len, size_t *ares)
{
#if CENV_SYS_UNIX || CENV_SYS_MOONMAC
register int res = read(fd, buf, len);
if (res < 0) {
if (ares) *ares = 0;
return FALSE;
}
#elif CENV_SYS_MAC
/* This is actually generic code for any system supporting unix-like
** calls with a 16-bit integer count interface.
*/
register size_t res = 0;
register unsigned int scnt, sres = 0;
while (len) {
scnt = len > (1<<14) ? (1<<14) : len; /* 16-bit count each whack */
if ((sres = read(fd, buf, cnt)) != scnt) {
if (sres == -1) { /* If didn't complete, check for err */
if (ares) *ares = res; /* Error, but may have read stuff */
return FALSE;
}
res += sres; /* No error, just update count */
break; /* and return successfully */
}
res += sres;
len -= sres;
buf += sres;
}
#endif
if (ares) *ares = res;
return TRUE;
}
int
os_fdwrite(osfd_t fd, char *buf, size_t len, size_t *ares)
{
#if CENV_SYS_UNIX || CENV_SYS_MOONMAC
register int res = write(fd, buf, len);
if (res < 0) {
if (ares) *ares = 0;
return FALSE;
}
#elif CENV_SYS_MAC
/* This is actually generic code for any system supporting unix-like
** calls with a 16-bit integer count interface.
*/
register size_t res = 0;
register unsigned int scnt, sres = 0;
while (len) {
scnt = len > (1<<14) ? (1<<14) : len; /* 16-bit count each whack */
if ((sres = write(fd, buf, cnt)) != scnt) {
if (sres == -1) { /* If didn't complete, check for err */
if (ares) *ares = res; /* Error, but may have written stuff */
return FALSE;
}
res += sres; /* No error, just update count */
break; /* and return successfully */
}
res += sres;
len -= sres;
buf += sres;
}
#endif
if (ares) *ares = res;
return TRUE;
}