Projects/tigris
Projects
/
tigris
Archived
5
0
Fork 0
This repository has been archived on 2024-07-04. You can view files and clone it, but cannot push or open issues or pull requests.
tigris/tikidisk-src/tikidisk.c

806 lines
20 KiB
C

/* tikidisk.c V1.1
*
* CP/M-2.2 filsystem som jobber mot TIKI-100 diskettfiler
*
* Burde fungere med alle CP/M-2.2 diskettfiler, bare riktige diskparametre oppgis.
* Foreløpig kjenner programmet bare igjen TIKI-100 diskettfiler.
*
* Copyright (c) Asbjørn Djupdal 2001
*/
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
typedef unsigned char byte;
typedef unsigned short word;
typedef short boolean;
#ifndef TRUE
#define TRUE ~0
#endif
#ifndef FALSE
#define FALSE 0
#endif
#ifndef NULL
#define NULL 0
#endif
#define DISK90K 1*40*18*128
#define DISK200K 1*40*10*512
#define DISK400K 2*40*10*512
#define DISK800K 2*80*10*512
/* protos */
int main (int argc, char *argv[]);
void saveFile (byte *entry);
void addFile (char *efn, byte user);
void delFile (char *ffn, byte user);
byte *getEntry (int entryNumber);
byte *getExtent (byte *entry, int extentNumber);
byte *getMatch (char *ffn, byte user);
byte *getSector (int sectorNumber);
int getExtentNumber (byte *entry);
void setExtentNumber (byte *entry, int extentNumber);
int getBytesInEntry (byte *entry);
void setBytesInEntry (byte *entry, int bytesInExtent);
int getBlockNumber (byte *entry, int alNumber);
void setBlockNumber (byte *entry, int alNumber, int blockNumber);
void getFilename (char *filename, byte *entry);
int getFilesize (byte *entry);
byte getUsernumber (byte *entry);
void getAttribs (char *attribs, byte *entry);
void setNameAndUser (byte *ffnEntry, char *ffn, byte user);
void buildBAM (void);
int allocateBlock (void);
boolean entryNotDeleted (byte *entry);
void deleteEntry (byte *entry);
boolean entryEquals (byte *ffnEntry, byte *entry);
char legalChar (char c);
char *stripPath (char *ffn);
/* globals */
byte diskImage[DISK800K]; /* her legges diskettfil */
boolean *bam = NULL; /* Block Availability Map */
int numberOfEntries; /* antall kataloginnganger */
int systemSectors; /* antall sektorer med bootkode */
int sectorSize; /* bytes pr. sektor */
int *convertSectorNumber; /* tabell som mapper sektornummer */
int sectorsPerTrack; /* antall sektorer pr. spor */
int exm; /* antall bytes pr. AL */
int bytesPerAL; /* blokkstørrelse */
int blockSize; /* bytes pr. blokk */
int sectorsPerBlock; /* sektorer pr. blokk */
int numberOfBlocks; /* antall blokker (untatt systemsektorer) */
int directoryBlocks; /* antall blokker brukt av kataloginnganger */
int numberOfALs; /* antall ALs pr. kataloginngang */
int convertSectorNumber18[18] = {0, 5, 10, 15, 2, 7, 12, 17, 4, 9, 14, 1, 6, 11, 16, 3, 8, 13};
int convertSectorNumber10[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
/* functions */
int main (int argc, char *argv[]) {
int size;
byte user;
if (argc < 4) {
fprintf (stderr, "tikidisk V1.1 Copyright (c) Asbjørn Djupdal 2001\n"
"\n"
"Brukes: tikidisk <diskfil> {lxad}[brukernummer] <filnavn...>\n"
" l = list filer\n"
" x = hent ut filer\n"
" a = legg til filer\n"
" d = slett filer\n"
"\n");
exit (1);
}
{ /* les diskfil */
FILE *diskImageFp;
if (!(diskImageFp = fopen (argv[1], "rb"))) {
fprintf (stderr, "Kan ikke åpne diskfil %s\n", argv[1]);
exit (1);
}
size = fread (diskImage, 1, DISK800K, diskImageFp);
fclose (diskImageFp);
}
/* sett diskparametre */
switch (size) {
case DISK90K:
convertSectorNumber = convertSectorNumber18;
numberOfEntries = 32;
systemSectors = 3 * 18;
sectorSize = 128;
sectorsPerTrack = 18;
exm = 0;
bytesPerAL = 1;
blockSize = 1024;
numberOfBlocks = 83;
directoryBlocks = 1;
break;
case DISK200K:
convertSectorNumber = convertSectorNumber10;
numberOfEntries = 64;
systemSectors = 20;
sectorSize = 512;
sectorsPerTrack = 10;
exm = 0;
bytesPerAL = 1;
blockSize = 1024;
numberOfBlocks = 190;
directoryBlocks = 2;
break;
case DISK400K:
convertSectorNumber = convertSectorNumber10;
numberOfEntries = 128;
systemSectors = 20;
sectorSize = 512;
sectorsPerTrack = 10;
exm = 1;
bytesPerAL = 1;
blockSize = 2048;
numberOfBlocks = 195;
directoryBlocks = 2;
break;
case DISK800K:
convertSectorNumber = convertSectorNumber10;
numberOfEntries = 128;
systemSectors = 20;
sectorSize = 512;
sectorsPerTrack = 10;
exm = 0;
bytesPerAL = 2;
blockSize = 2048;
numberOfBlocks = 395;
directoryBlocks = 2;
break;
default:
fprintf (stderr, "Ikke en gyldig diskfil\n");
exit (1);
}
sectorsPerBlock = blockSize / sectorSize;
numberOfALs = 16 / bytesPerAL;
{ /* finn brukernummer */
char *ptr;
user = (byte)strtol (argv[2] + 1, &ptr, 10);
if (ptr == argv[2] + 1) { /* brukernummer ikke angitt */
user = (byte)'?';
} else if (user > 15) {
fprintf (stderr, "Ulovlig brukernummer\n");
exit (1);
}
}
/* utfør kommando */
switch (*argv[2]) {
case 'l': {
/* list filer */
int files = 0;
int bytes = 0;
byte *entry;
if ((entry = getMatch (argv[3], user))) {
do {
char filename[13];
char attribs[4];
int filesize = getFilesize (entry);
getFilename (filename, entry);
getAttribs (attribs, entry);
printf (" %6d %s %02d: %s\n", filesize, attribs, getUsernumber (entry), filename);
files++;
bytes += filesize;
} while ((entry = getMatch (NULL, 0)));
}
printf ("%d filer, %d bytes\n", files, bytes);
break;
}
case 'x': {
/* lagre filer */
int i = 3;
while (argv[i]) {
byte *entry;
if ((entry = getMatch (argv[i], user))) {
do {
saveFile (entry);
} while ((entry = getMatch (NULL, 0)));
}
i++;
}
break;
}
case 'a': {
/* legg til filer */
int i = 3;
if (user == (byte)'?') {
user = 0;
}
while (argv[i]) {
addFile (argv[i], user);
i++;
}
break;
}
case 'd': {
/* slett filer */
int i = 3;
while (argv[i]) {
delFile (argv[i], user);
i++;
}
break;
}
default:
fprintf (stderr, "Ulovlig kommando\n");
exit (1);
}
/* lagre diskfil */
if ((*argv[2] == 'a') || (*argv[2] == 'd')) {
FILE *diskImageFp;
if ((diskImageFp = fopen (argv[1], "wb"))) {
fwrite (diskImage, 1, size, diskImageFp);
fclose (diskImageFp);
}
}
free (bam);
return 0;
}
/* Trekker ut fil fra diskettfil og lagrer på disk
*
* entry: en ekstent til filen som skal lagres
*/
void saveFile (byte *entry) {
char filename[13];
FILE *fp;
getFilename (filename, entry);
if ((fp = fopen (filename, "wb"))) {
int extentNumber = 0;
printf ("Henter ut %02d:%s...\n", getUsernumber (entry), filename);
/* gå gjennom alle extenter */
while ((entry = getExtent (entry, extentNumber++))) {
boolean done = FALSE;
int blockNumber;
int alNumber = 0;
int bytesRemaining = getBytesInEntry (entry);
/* gå gjennom alle blokker */
while (!done && (alNumber < numberOfALs)) {
if ((blockNumber = getBlockNumber (entry, alNumber))) {
int i;
/* lagre sektorer i blokk */
for (i = 0; i < sectorsPerBlock; i++) {
int sectorNumber = ((blockNumber * blockSize) / sectorSize) + systemSectors + i;
if (bytesRemaining) {
int bytesToWrite = (bytesRemaining < sectorSize ? bytesRemaining : sectorSize);
fwrite (getSector (sectorNumber), 1, bytesToWrite, fp);
bytesRemaining -= bytesToWrite;
} else {
done = TRUE;
}
}
}
alNumber++;
}
}
fclose (fp);
}
}
/* Henter inn fil fra disk og legger inn i diskettfil
*
* efn: entydig filnavn til fil som skal legges til
* user: entydig brukernummer som fil skal lagres under
*/
void addFile (char *efn, byte user) {
FILE *fp;
byte *entry = NULL;
int extentNumber = 0;
int alNumber = 0;
int sectorNumber = 0;
int bytesInExtent = 0;
byte *sectorData;
boolean done = FALSE;
if (!(fp = fopen (efn, "rb"))) {
fprintf (stderr, "Kan ikke åpne %s\n", efn);
return;
}
if (!(sectorData = malloc (sectorSize))) {
fprintf (stderr, "Ikke nok minne\n");
free (bam);
exit (1);
}
delFile (stripPath (efn), user);
buildBAM();
printf ("Legger til %02d:%s...\n", user, efn);
while (!done) {
int bytesRead = fread (sectorData, 1, sectorSize, fp);
if (bytesRead) {
/* finn ledig kataloginngang */
if (!(bytesInExtent % (numberOfALs * blockSize))) {
int entryNumber = 0;
if (entry) setBytesInEntry (entry, bytesInExtent);
do {
if (entryNumber >= numberOfEntries) {
fprintf (stderr, "Ikke nok ledige kataloginnganger\n");
free (sectorData);
free (bam);
exit (1);
}
entry = getEntry (entryNumber);
entryNumber++;
} while (entryNotDeleted (entry));
memset (entry, 0, 32);
setNameAndUser (entry, stripPath (efn), user);
setExtentNumber (entry, extentNumber++);
alNumber = 0;
bytesInExtent = 0;
}
/* finn ledig blokk */
if (!(bytesInExtent % blockSize)) {
int blockNumber;
if (!(blockNumber = allocateBlock())) {
fprintf (stderr, "Ikke nok ledige blokker\n");
free (sectorData);
free (bam);
exit (1);
}
setBlockNumber (entry, alNumber++, blockNumber);
sectorNumber = ((blockNumber * blockSize) / sectorSize) + systemSectors;
}
/* skriv til sektor */
memcpy (getSector (sectorNumber++), sectorData, sectorSize);
bytesInExtent += bytesRead;
} else {
done = TRUE;
setBytesInEntry (entry, bytesInExtent);
}
}
free (sectorData);
}
/* Sletter filer fra diskettfil
*
* ffn: flertydig filnavn til filer som skal slettes
* user: flertydig brukernummer til filer som skal slettes
*/
void delFile (char *ffn, byte user) {
byte *entry;
if ((entry = getMatch (ffn, user))) {
/* gå gjennom alle entries (filer) */
do {
int extentNumber = 0;
byte *extent;
char filename[13];
getFilename (filename, entry);
printf ("Sletter %02d:%s...\n", getUsernumber (entry), filename);
/* slett alle extenter til filen */
while ((extent = getExtent (entry, extentNumber++))) {
deleteEntry (extent);
}
} while ((entry = getMatch (NULL, 0)));
}
}
/* Gir peker til entry i diskettfil
*
* entryNumber: nummer på entry som skal returneres
*/
byte *getEntry (int entryNumber) {
int dirSector = systemSectors + ((entryNumber * 32) / sectorSize);
int offsetInSector = (entryNumber * 32) % sectorSize;
return getSector (dirSector) + offsetInSector;
}
/* Gir peker til extent i diskettfil, eller NULL derom ikke funnet
*
* entry: en allerede kjent extent
* extentNumber: nummer til ønsket extent
*/
byte *getExtent (byte *entry, int extentNumber) {
int entryNumber;
for (entryNumber = 0; entryNumber < numberOfEntries; entryNumber++) {
byte *curEntry = getEntry (entryNumber);
if (entryNotDeleted (curEntry) && entryEquals (entry, curEntry) &&
(getExtentNumber (curEntry) == extentNumber)) {
return curEntry;
}
}
return NULL;
}
/* Gir peker til neste fil (extent 0) som passer, eller NULL dersom ikke flere passer
*
* ffn: flertydig filnavn til filer som skal finnes
* Angi ffn kun første gang funksjonen kalles. For å finne de resterende filer
* som passer, kall funksjonen med ffn = NULL
* user: flertydig brukernummer til filer som skal finnes
*/
byte *getMatch (char *ffn, byte user) {
static byte ffnEntry[12];
static int entryNumber;
if (ffn != NULL) {
entryNumber = 0;
setNameAndUser (ffnEntry, ffn, user);
}
while (entryNumber < numberOfEntries) {
byte *entry = getEntry (entryNumber);
entryNumber++;
if (entryNotDeleted (entry) && entryEquals (ffnEntry, entry) && (getExtentNumber (entry) == 0)) {
return entry;
}
}
return NULL;
}
/* Gir peker til stedet der data for angitt sektor befinner seg
*
* sectorNumber: nummer på sektor som ønskes
*/
byte *getSector (int sectorNumber) {
int realSectorNumber = (sectorNumber / sectorsPerTrack) * sectorsPerTrack +
convertSectorNumber[sectorNumber % sectorsPerTrack];
return diskImage + realSectorNumber * sectorSize;
}
/* Gir nummeret på extent (fra EX og S2 feltene)
*
* entry: kataloginngang man ønsker extentnummeret til
*/
int getExtentNumber (byte *entry) {
return (*(entry + 14) * 32 + *(entry + 12)) / (exm + 1);
}
/* Setter nummer på extent (til EX og S2 feltene)
*
* entry: kataloginngang man skal sette extentnummeret til
* extentNumber: extentnummeret som skal settes
*/
void setExtentNumber (byte *entry, int extentNumber) {
*(entry + 14) = extentNumber / 32;
*(entry + 12) = ((extentNumber % 32) * (exm + 1));
}
/* Gir antall bytes lagret i extent (fra RC og EX feltene)
*
* entry: extent man ønsker størrelsen til
*/
int getBytesInEntry (byte *entry) {
return ((*(entry + 12) & exm) * 128 + *(entry + 15)) * 128;
}
/* Setter antall bytes lagret i extent (til RC og EX feltene)
*
* entry: kataloginngang man skal sette størrelsen til
* bytesInExtent: antall bytes som skal settes
*/
void setBytesInEntry (byte *entry, int bytesInExtent) {
int recordsInExtent = bytesInExtent / 128;
if (bytesInExtent % 128) recordsInExtent++;
if (recordsInExtent % 0x80) {
*(entry + 15) = recordsInExtent % 0x80;
} else {
*(entry + 15) = 0x80;
}
if (recordsInExtent > 0x80) {
*(entry + 12) += (recordsInExtent / 0x80);
}
}
/* Gir nummeret på blokken som befinner seg i en AL
*
* entry: kataloginngang
* alNumber: nummer på AL man ønsker blokknummeret til
*/
int getBlockNumber (byte *entry, int alNumber) {
int al1 = *(entry + 16 + (alNumber * bytesPerAL));
int al2 = *(entry + 16 + (alNumber * bytesPerAL) + 1);
return (bytesPerAL == 1 ? al1 : al1 + (al2 * 256));
}
/* Setter nummer på blokk i an AL
*
* entry: kataloginngang
* alNumber: nummer på AL
* blockNumber: nummer på blokk
*/
void setBlockNumber (byte *entry, int alNumber, int blockNumber) {
if (bytesPerAL < 2) {
*(entry + 16 + alNumber) = blockNumber;
} else {
*(entry + 16 + (alNumber * 2)) = blockNumber;
*(entry + 16 + (alNumber * 2) + 1) = blockNumber / 256;
}
}
/* Kopierer filnavn fra en kataloginngang til en streng
*
* filename: streng som filnavn skal kopieres til
* entry: kataloginngang man ønsker filnavnet til
*/
void getFilename (char *filename, byte *entry) {
int i = 0, j = 0;
entry++;
/* kopier filnavn */
while ((i < 8) && (*entry != ' ')) {
filename[i++] = legalChar (*entry++);
}
entry += 8 - i;
filename[i++] = '.';
/* kopier filtype */
while ((j++ < 3) && (*entry != ' ')) {
filename[i++] = legalChar ((*entry++) & 0x7f);
}
filename[i] = '\0';
}
/* Gir størrelsen på en fil
*
* entry: en extent til fila man ønsker størrelsen til
*/
int getFilesize (byte *entry) {
int filesize = 0;
boolean done = FALSE;
int extentNumber = 0;
while (!done) {
if (!(entry = getExtent (entry, extentNumber++))) {
done = TRUE;
} else {
filesize += getBytesInEntry (entry);
}
}
return filesize;
}
/* Gir brukernummeret til en kataloginngang
*
* entry: kataloginngang man ønsker brukernummeret til
*/
byte getUsernumber (byte *entry) {
return *entry;
}
/* Lager en streng som representerer attributtene til en kataloginngang
*
* attribs: streng som attributtinfo skal skrives til
* entry: kataloginngang man ønsker attributtinfo til
*/
void getAttribs (char *attribs, byte *entry) {
*(attribs + 0) = (*(entry + 9)) & 0x80 ? 'l' : '-';
*(attribs + 1) = (*(entry + 10)) & 0x80 ? 's' : '-';
*(attribs + 2) = (*(entry + 11)) & 0x80 ? 'a' : '-';
*(attribs + 3) = '\0';
}
/* Kopierer filnavn og brukernummer inn i en kataloginngang
*
* ffnEntry: kataloginngang man skal skrive til
* ffn: flertydig filnavn som skal skrives fra
* user: flertydig brukernummer som skal skrives fra
*/
void setNameAndUser (byte *ffnEntry, char *ffn, byte user) {
int i;
/* kopier brukernummer */
*ffnEntry++ = user;
/* kopier filnavn */
for (i = 0; i < 8; i++, ffnEntry++) {
if (*ffn == '*') {
*ffnEntry = (byte)'?';
} else if (*ffn == '?') {
*ffnEntry = (byte)'?';
ffn++;
} else if (*ffn == '.') {
*ffnEntry = (byte)' ';
} else if (*ffn == '\0') {
*ffnEntry = (byte)' ';
} else {
*ffnEntry = (byte)legalChar (*ffn);
ffn++;
}
}
/* finn filtype */
if (strchr (ffn, '.')) {
ffn = strchr (ffn, '.') + 1;
/* kopier filtype */
for (i = 0; i < 3; i++) {
if (*ffn == '*') {
*ffnEntry++ = (byte)'?';
} else if (*ffn == '?') {
*ffnEntry++ = (byte)'?';
ffn++;
} else if (*ffn == '.') {
ffn++;
} else if (*ffn == '\0') {
*ffnEntry++ = (byte)' ';
} else {
*ffnEntry++ = (byte)legalChar (*ffn);
ffn++;
}
}
}
}
/* Bygger Block Availability Map for diskettfil
*/
void buildBAM (void) {
int entryNumber;
free (bam);
if (!(bam = malloc (sizeof (boolean) * numberOfBlocks))) {
fprintf (stderr, "Ikke nok minne\n");
exit (1);
}
memset (bam, 0, sizeof (boolean) * numberOfBlocks);
{ /* merk katalogblokker som opptatte */
int blockNumber;
for (blockNumber = 0; blockNumber < directoryBlocks; blockNumber++) {
*(bam + blockNumber) = TRUE;
}
}
/* gå gjennom alle kataloginnganger */
for (entryNumber = 0; entryNumber < numberOfEntries; entryNumber++) {
byte *entry = getEntry (entryNumber);
if (entryNotDeleted (entry)) {
int blockNumber;
int alNumber = 0;
/* merk alle blokker som denne kataloginngangen bruker som opptatt */
while ((alNumber < numberOfALs) && (blockNumber = getBlockNumber (entry, alNumber))) {
*(bam + blockNumber) = TRUE;
alNumber++;
}
}
}
}
/* Allokerer en blokk og gir nummeret på den nye blokken
*/
int allocateBlock (void) {
int blockNumber;
for (blockNumber = 0; blockNumber < numberOfBlocks; blockNumber++) {
if (!*(bam + blockNumber)) {
*(bam + blockNumber) = TRUE;
return blockNumber;
}
}
return 0;
}
/* Gir slettet-status til en kataloginngang
* entry: kataloginngang
*/
boolean entryNotDeleted (byte *entry) {
return (*entry < 32);
}
/* Sletter en kataloginngang
* entry: kataloginngang
*/
void deleteEntry (byte *entry) {
*entry = 0xe5;
}
/* Sjekker om to kataloginnganger har samme navn og brukernummer
*
* ffnEntry: kataloginngang med flertydig navn og brukernummer
* entry: kataloginngang som ffnEntry skal sjekkes mot
*/
boolean entryEquals (byte *ffnEntry, byte *entry) {
if (((*(ffnEntry + 0) == '?') || (*(ffnEntry + 0) == *(entry + 0))) &&
((*(ffnEntry + 1) == '?') || (*(ffnEntry + 1) == *(entry + 1))) &&
((*(ffnEntry + 2) == '?') || (*(ffnEntry + 2) == *(entry + 2))) &&
((*(ffnEntry + 3) == '?') || (*(ffnEntry + 3) == *(entry + 3))) &&
((*(ffnEntry + 4) == '?') || (*(ffnEntry + 4) == *(entry + 4))) &&
((*(ffnEntry + 5) == '?') || (*(ffnEntry + 5) == *(entry + 5))) &&
((*(ffnEntry + 6) == '?') || (*(ffnEntry + 6) == *(entry + 6))) &&
((*(ffnEntry + 7) == '?') || (*(ffnEntry + 7) == *(entry + 7))) &&
((*(ffnEntry + 8) == '?') || (*(ffnEntry + 8) == *(entry + 8))) &&
((*(ffnEntry + 9) == '?') || (*(ffnEntry + 9) == *(entry + 9))) &&
((*(ffnEntry + 10) == '?') || (*(ffnEntry + 10) == *(entry + 10))) &&
((*(ffnEntry + 11) == '?') || (*(ffnEntry + 11) == *(entry + 11)))) {
return TRUE;
}
return FALSE;
}
/* Konverterer et tegn til et tegn som kan brukes i alle filnavn på så godt
* som alle operativsystem
*
* c: tegn som skal konverteres
*/
char legalChar (char c) {
const char *legalChars = "_^$~!#&-{}@'";
if (!strchr (legalChars, c)) {
if ((c < 48) || (c > 57 && c < 65) || (c > 90 && c < 97) || (c >122)) {
return '#';
}
}
return toupper (c);
}
/* Gir en peker til første tegn i filnavnet (etter evt. sti)
*
* ffn: filnavn med evt. sti
*/
char *stripPath (char *ffn) {
char *ptr;
if (!(ptr = strrchr (ffn, '/'))) {
if (!(ptr = strrchr (ffn, '\\'))) {
if (!(ptr = strrchr (ffn, ':'))) {
ptr = ffn - 1;
}
}
}
return ptr + 1;
}