mirror of
https://github.com/ksherlock/profuse.git
synced 2025-01-09 00:30:45 +00:00
initial checkin
git-svn-id: https://profuse.googlecode.com/svn/trunk@4 aa027e90-d47c-11dd-86d7-074df07e0730
This commit is contained in:
parent
90bf49881f
commit
bc7c5d73bb
546
Disk.cpp
Normal file
546
Disk.cpp
Normal file
@ -0,0 +1,546 @@
|
||||
/*
|
||||
* Disk.cpp
|
||||
* ProFUSE
|
||||
*
|
||||
* Created by Kelvin Sherlock on 12/18/08.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "Disk.h"
|
||||
#include "common.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
struct ucmp
|
||||
{
|
||||
bool operator()(unsigned a, unsigned b) const
|
||||
{
|
||||
return a < b;
|
||||
}
|
||||
};
|
||||
|
||||
using std::set;
|
||||
using std::vector;
|
||||
|
||||
typedef set<unsigned, ucmp> uset;
|
||||
|
||||
Disk::Disk()
|
||||
{
|
||||
_data = (uint8_t *)-1;
|
||||
_blocks = 0;
|
||||
_offset = 0;
|
||||
_size = 0;
|
||||
}
|
||||
|
||||
Disk::~Disk()
|
||||
{
|
||||
if (_data != (uint8_t *)-1)
|
||||
munmap(_data, _size);
|
||||
}
|
||||
|
||||
|
||||
Disk *Disk::OpenFile(const char *file)
|
||||
{
|
||||
int fd;
|
||||
struct stat st;
|
||||
size_t size;
|
||||
unsigned blocks;
|
||||
|
||||
unsigned offset;
|
||||
|
||||
void *map;
|
||||
Disk *d = NULL;
|
||||
|
||||
fd = open(file, O_RDONLY);
|
||||
if (fd >= 0)
|
||||
{
|
||||
|
||||
if (fstat(fd, &st) == 0)
|
||||
{
|
||||
size = st.st_size;
|
||||
|
||||
// raw disk images must be a blocksize multiple and <= 32 Meg.
|
||||
|
||||
if ( (size & 0x1ff) == 0
|
||||
&& size > 0
|
||||
&& size <= 32 * 1024 * 1024
|
||||
)
|
||||
{
|
||||
blocks = size >> 9;
|
||||
offset = 0;
|
||||
}
|
||||
else {
|
||||
// check for disk copy 4.2:
|
||||
// 800k disk, but there's a 84-byte header
|
||||
// and possible tag data (???)
|
||||
// +80 = 0x01 (800K Disk)
|
||||
// +81 = 0x24 (800K ProDOS disk)
|
||||
// +82 = 0x01
|
||||
// +83 = 0x00
|
||||
uint8_t buffer[1024];
|
||||
|
||||
if (read(fd, buffer, 1024) != 1024)
|
||||
{
|
||||
close(fd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (size == 819284
|
||||
&& buffer[80] == 0x01
|
||||
&& buffer[81] == 0x24
|
||||
&& buffer[82] == 0x01
|
||||
&& buffer[83] == 0x00)
|
||||
{
|
||||
// Disk Copy.
|
||||
// currently ignoring the checksum.
|
||||
blocks = 819284 >> 9;
|
||||
offset = 84;
|
||||
}
|
||||
// check 2mg.
|
||||
else if (buffer[0] == '2'
|
||||
&& buffer[1] == 'I'
|
||||
&& buffer[2] == 'M'
|
||||
&& buffer[3] == 'G'
|
||||
&& buffer[0x0c] == 0x01 // ProDOS order
|
||||
)
|
||||
{
|
||||
//
|
||||
blocks = load32(&buffer[0x14]);
|
||||
offset = load32(&buffer[0x18]);
|
||||
}
|
||||
else
|
||||
{
|
||||
close(fd);
|
||||
return NULL;
|
||||
}
|
||||
lseek(fd, 0, SEEK_SET);
|
||||
}
|
||||
|
||||
|
||||
map = mmap(NULL, size, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0);
|
||||
if (map != (void *)-1)
|
||||
{
|
||||
d = new Disk();
|
||||
d->_size = size;
|
||||
d->_data = (uint8_t *)map;
|
||||
d->_blocks = blocks;
|
||||
d->_offset = offset;
|
||||
}
|
||||
|
||||
}
|
||||
close(fd);
|
||||
}
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
|
||||
// load the mini entry into the regular entry.
|
||||
int Disk::Normalize(FileEntry &f, unsigned fork, ExtendedEntry *ee)
|
||||
{
|
||||
uint8_t buffer[BLOCK_SIZE];
|
||||
int ok;
|
||||
|
||||
if (fork > 1) return -P8_INVALID_FORK;
|
||||
|
||||
if (f.storage_type != EXTENDED_FILE)
|
||||
{
|
||||
return fork == 0 ? 0 : -P8_INVALID_FORK;
|
||||
}
|
||||
|
||||
ok = Read(f.key_pointer, buffer);
|
||||
if (ok < 0) return ok;
|
||||
|
||||
ExtendedEntry e(buffer);
|
||||
|
||||
if (fork == 0)
|
||||
{
|
||||
f.storage_type = e.dataFork.storage_type;
|
||||
f.key_pointer = e.dataFork.key_block;
|
||||
f.eof = e.dataFork.eof;
|
||||
f.blocks_used = e.dataFork.blocks_used;
|
||||
}
|
||||
else
|
||||
{
|
||||
f.storage_type = e.resourceFork.storage_type;
|
||||
f.key_pointer = e.resourceFork.key_block;
|
||||
f.eof = e.resourceFork.eof;
|
||||
f.blocks_used = e.resourceFork.blocks_used;
|
||||
}
|
||||
|
||||
if (ee) *ee = e;
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
|
||||
int Disk::Read(unsigned block, void *buffer)
|
||||
{
|
||||
if (block > _blocks) return -P8_INVALID_BLOCK;
|
||||
memcpy(buffer, _data + _offset + (block << 9), BLOCK_SIZE);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
void *Disk::ReadFile(const FileEntry &f, unsigned fork, uint32_t *size, int *error)
|
||||
{
|
||||
|
||||
#define SET_ERROR(x) if (error) *error = (x)
|
||||
#define SET_SIZE(x) if (size) *size = (x)
|
||||
|
||||
|
||||
SET_ERROR(0);
|
||||
SET_SIZE(0);
|
||||
|
||||
if (fork != DATA_FORK && fork != RESOURCE_FORK)
|
||||
{
|
||||
SET_ERROR(-P8_INVALID_FORK);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint8_t buffer[BLOCK_SIZE];
|
||||
int ok;
|
||||
uint32_t eof;
|
||||
uint32_t alloc;
|
||||
unsigned blocks;
|
||||
unsigned storage_type;
|
||||
unsigned key_block;
|
||||
|
||||
switch(f.storage_type)
|
||||
{
|
||||
case SEEDLING_FILE:
|
||||
case SAPLING_FILE:
|
||||
case TREE_FILE:
|
||||
if (fork != DATA_FORK)
|
||||
{
|
||||
SET_ERROR(1);
|
||||
return NULL;
|
||||
}
|
||||
storage_type = f.storage_type;
|
||||
eof = f.eof;
|
||||
key_block = f.key_pointer;
|
||||
break;
|
||||
|
||||
case EXTENDED_FILE:
|
||||
{
|
||||
ok = Read(f.key_pointer, buffer);
|
||||
if (ok < 0)
|
||||
{
|
||||
SET_ERROR(ok);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ExtendedEntry entry(buffer);
|
||||
|
||||
if (fork == DATA_FORK)
|
||||
{
|
||||
storage_type = entry.dataFork.storage_type;
|
||||
eof = entry.dataFork.eof;
|
||||
key_block = entry.dataFork.key_block;
|
||||
}
|
||||
else
|
||||
{
|
||||
storage_type = entry.resourceFork.storage_type;
|
||||
eof = entry.resourceFork.eof;
|
||||
key_block = entry.resourceFork.key_block;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
SET_ERROR(-P8_INVALID_STORAGE_TYPE);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (eof == 0)
|
||||
{
|
||||
SET_ERROR(1);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
blocks = (eof + BLOCK_SIZE - 1) >> 9;
|
||||
alloc = (eof + BLOCK_SIZE - 1) & (~BLOCK_SIZE);
|
||||
uint8_t* data = new uint8_t[alloc];
|
||||
|
||||
|
||||
switch (storage_type)
|
||||
{
|
||||
case SEEDLING_FILE:
|
||||
ok = Read(key_block, data);
|
||||
break;
|
||||
case SAPLING_FILE:
|
||||
ok = ReadIndex(f.key_pointer, buffer, 1, 0, blocks);
|
||||
break;
|
||||
case TREE_FILE:
|
||||
ok = ReadIndex(f.key_pointer, buffer, 2, 0, blocks);
|
||||
break;
|
||||
}
|
||||
|
||||
if (ok < 0)
|
||||
{
|
||||
SET_ERROR(ok);
|
||||
delete[] data;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
bzero(data + eof, alloc - eof);
|
||||
SET_SIZE(eof);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
int Disk::ReadFile(const FileEntry &f, void *buffer)
|
||||
{
|
||||
int blocks = (f.eof + BLOCK_SIZE - 1) >> 9;
|
||||
int ok;
|
||||
|
||||
switch(f.storage_type)
|
||||
{
|
||||
case TREE_FILE:
|
||||
ok = ReadIndex(f.key_pointer, buffer, 2, 0, blocks);
|
||||
break;
|
||||
case SAPLING_FILE:
|
||||
ok = ReadIndex(f.key_pointer, buffer, 1, 0, blocks);
|
||||
break;
|
||||
case SEEDLING_FILE:
|
||||
ok = Read(f.key_pointer, buffer);
|
||||
break;
|
||||
|
||||
default:
|
||||
return -P8_INVALID_STORAGE_TYPE;
|
||||
}
|
||||
|
||||
if (ok >= 0)
|
||||
{
|
||||
bzero((uint8_t *)buffer + f.eof, (blocks << 9) - f.eof);
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
|
||||
int Disk::ReadIndex(unsigned block, void *buffer, unsigned level, off_t offset, unsigned blocks)
|
||||
{
|
||||
// does not yet handle sparse files (completely).
|
||||
|
||||
if (level == 0)
|
||||
{
|
||||
// data level
|
||||
if (block == 0) // sparse file
|
||||
{
|
||||
bzero(buffer, BLOCK_SIZE);
|
||||
return 1;
|
||||
}
|
||||
return Read(block, buffer);
|
||||
}
|
||||
|
||||
|
||||
unsigned blockCount;
|
||||
unsigned readSize;
|
||||
unsigned first;
|
||||
//unsigned last;
|
||||
|
||||
|
||||
switch(level)
|
||||
{
|
||||
case 1:
|
||||
first = (offset >> 9) & 0xff;
|
||||
blockCount = 1;
|
||||
readSize = BLOCK_SIZE;
|
||||
offset = 0;
|
||||
break;
|
||||
case 2:
|
||||
first = (offset >> 17) & 0xff;
|
||||
blockCount = 256;
|
||||
readSize = BLOCK_SIZE << 8;
|
||||
offset &= 0x1ffff;
|
||||
break;
|
||||
default:
|
||||
return -P8_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
int ok;
|
||||
|
||||
uint8_t key[BLOCK_SIZE];
|
||||
ok = Read(block, key);
|
||||
if (ok < 0 ) return ok;
|
||||
|
||||
|
||||
for (unsigned i = first; blocks; i++)
|
||||
{
|
||||
// block pointers are split up since 8-bit indexing is limited to 256.
|
||||
unsigned newBlock = (key[i]) | (key[256 + i] << 8);
|
||||
|
||||
unsigned b = std::min(blocks, blockCount);
|
||||
|
||||
ok = ReadIndex(newBlock, buffer, level - 1, offset, b);
|
||||
if (ok < 0) return ok;
|
||||
offset = 0;
|
||||
buffer = ((char *)buffer) + readSize;
|
||||
blocks -= b;
|
||||
}
|
||||
return blocks;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int Disk::ReadVolume(VolumeEntry *volume, std::vector<FileEntry> *files)
|
||||
{
|
||||
if (files) files->resize(0);
|
||||
|
||||
uint8_t buffer[BLOCK_SIZE];
|
||||
int ok;
|
||||
unsigned prev;
|
||||
unsigned next;
|
||||
|
||||
uset blocks;
|
||||
|
||||
unsigned block = 2;
|
||||
blocks.insert(block);
|
||||
ok = Read(block, buffer);
|
||||
|
||||
if (ok < 0) return ok;
|
||||
|
||||
prev = load16(&buffer[0x00]);
|
||||
next = load16(&buffer[0x02]);
|
||||
|
||||
VolumeEntry v(buffer + 0x04);
|
||||
|
||||
if (v.storage_type != VOLUME_HEADER) return -P8_INVALID_STORAGE_TYPE;
|
||||
|
||||
if (volume) *volume = v;
|
||||
|
||||
if (!files) return 1;
|
||||
|
||||
if (v.file_count)
|
||||
{
|
||||
files->reserve(v.file_count);
|
||||
//files->resize(v.file_count);
|
||||
|
||||
//unsigned count = 0;
|
||||
unsigned index = 1; // skip the header.
|
||||
for(;;)
|
||||
{
|
||||
//
|
||||
if ( (buffer[0x04 + v.entry_length * index] >> 4) != DELETED_FILE)
|
||||
{
|
||||
unsigned offset = v.entry_length * index + 0x4;
|
||||
FileEntry f(buffer + offset);
|
||||
f.address = (block << 9) + offset;
|
||||
|
||||
files->push_back(f);
|
||||
//if (++count == v.file_count) break;
|
||||
}
|
||||
index++;
|
||||
if (index >= v.entries_per_block)
|
||||
{
|
||||
if (!next) break; // all done!
|
||||
|
||||
|
||||
if (blocks.insert(next).second == false)
|
||||
{
|
||||
return -P8_CYCLICAL_BLOCK;
|
||||
}
|
||||
|
||||
ok = Read(next, buffer);
|
||||
if (ok < 0) return ok;
|
||||
block = next;
|
||||
|
||||
prev = load16(&buffer[0x00]);
|
||||
next = load16(&buffer[0x02]);
|
||||
|
||||
index = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
int Disk::ReadDirectory(unsigned block, SubdirEntry *dir, std::vector<FileEntry> *files)
|
||||
{
|
||||
if (files) files->resize(0);
|
||||
|
||||
uint8_t buffer[BLOCK_SIZE];
|
||||
int ok;
|
||||
unsigned prev;
|
||||
unsigned next;
|
||||
|
||||
// keep a list of blocks to prevent cyclical problems.
|
||||
uset blocks;
|
||||
|
||||
blocks.insert(block);
|
||||
|
||||
ok = Read(block, buffer);
|
||||
|
||||
if (ok < 0) return ok;
|
||||
|
||||
prev = load16(&buffer[0x00]);
|
||||
next = load16(&buffer[0x02]);
|
||||
|
||||
SubdirEntry v(buffer + 0x04);
|
||||
|
||||
if (v.storage_type != SUBDIR_HEADER) return -P8_INVALID_STORAGE_TYPE;
|
||||
|
||||
|
||||
if (dir) *dir = v;
|
||||
|
||||
if (!files) return 1;
|
||||
|
||||
if (v.file_count)
|
||||
{
|
||||
files->reserve(v.file_count);
|
||||
//files->resize(v.file_count);
|
||||
|
||||
//unsigned count = 0;
|
||||
unsigned index = 1; // skip the header.
|
||||
for(;;)
|
||||
{
|
||||
//
|
||||
if ( (buffer[0x04 + v.entry_length * index] >> 4) != DELETED_FILE)
|
||||
{
|
||||
unsigned offset = v.entry_length * index + 0x4;
|
||||
FileEntry f(buffer + offset);
|
||||
f.address = (block << 9) + offset;
|
||||
|
||||
files->push_back(f);
|
||||
|
||||
//if (++count == v.file_count) break;
|
||||
}
|
||||
index++;
|
||||
if (index >= v.entries_per_block)
|
||||
{
|
||||
if (!next) break; // all done!
|
||||
|
||||
|
||||
if (blocks.insert(next).second == false)
|
||||
{
|
||||
return -P8_CYCLICAL_BLOCK;
|
||||
}
|
||||
|
||||
ok = Read(next, buffer);
|
||||
if (ok < 0) return ok;
|
||||
block = next;
|
||||
|
||||
|
||||
prev = load16(&buffer[0x00]);
|
||||
next = load16(&buffer[0x02]);
|
||||
|
||||
index = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
66
Disk.h
Normal file
66
Disk.h
Normal file
@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Disk.h
|
||||
* ProFUSE
|
||||
*
|
||||
* Created by Kelvin Sherlock on 12/18/08.
|
||||
*
|
||||
*/
|
||||
#ifndef __DISK_H__
|
||||
#define __DISK_H__
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "File.h"
|
||||
|
||||
|
||||
enum {
|
||||
P8_OK = 0,
|
||||
P8_INTERNAL_ERROR,
|
||||
P8_INVALID_FORK,
|
||||
P8_INVALID_BLOCK,
|
||||
P8_INVALID_STORAGE_TYPE,
|
||||
P8_CYCLICAL_BLOCK
|
||||
|
||||
};
|
||||
|
||||
enum {
|
||||
DATA_FORK = 0,
|
||||
RESOURCE_FORK = 1
|
||||
};
|
||||
|
||||
|
||||
|
||||
class Disk {
|
||||
|
||||
public:
|
||||
~Disk();
|
||||
|
||||
//static Disk *Open2MG(const char *file);
|
||||
static Disk *OpenFile(const char *file);
|
||||
|
||||
|
||||
int Normalize(FileEntry &f, unsigned fork, ExtendedEntry *ee = NULL);
|
||||
|
||||
int Read(unsigned block, void *buffer);
|
||||
int ReadIndex(unsigned block, void *buffer, unsigned level, off_t offset, unsigned blocks);
|
||||
|
||||
int ReadFile(const FileEntry &f, void *buffer);
|
||||
|
||||
void *ReadFile(const FileEntry &f, unsigned fork, uint32_t *size, int * error);
|
||||
|
||||
|
||||
int ReadVolume(VolumeEntry *volume, std::vector<FileEntry> *files);
|
||||
int ReadDirectory(unsigned block, SubdirEntry *dir, std::vector<FileEntry> *files);
|
||||
|
||||
private:
|
||||
Disk();
|
||||
uint8_t *_data;
|
||||
unsigned _offset;
|
||||
unsigned _blocks;
|
||||
size_t _size;
|
||||
};
|
||||
|
||||
#endif
|
275
File.cpp
Normal file
275
File.cpp
Normal file
@ -0,0 +1,275 @@
|
||||
/*
|
||||
* File.cpp
|
||||
* ProFUSE
|
||||
*
|
||||
* Created by Kelvin Sherlock on 12/18/08.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "File.h"
|
||||
|
||||
#include "common.h"
|
||||
#include <strings.h>
|
||||
#include <stdint.h>
|
||||
#include <ctype.h>
|
||||
|
||||
|
||||
/*
|
||||
* ProDOS technote 28
|
||||
*
|
||||
* The following definition allows the same range of years that the Apple IIgs
|
||||
* Control Panel CDA currently does:
|
||||
*
|
||||
* o A seven-bit ProDOS year value is in the range 0 to 99
|
||||
* (100 through 127 are invalid)
|
||||
* o Year values from 40 to 99 represent 1940 through 1999
|
||||
* o Year values from 0 to 39 represent 2000 through 2039
|
||||
*/
|
||||
inline time_t timeToUnix(unsigned yymmdd, unsigned hhmm)
|
||||
{
|
||||
if (yymmdd == 0) return 0;
|
||||
|
||||
tm t;
|
||||
bzero(&t, sizeof(tm));
|
||||
|
||||
t.tm_min = hhmm & 0x3f;
|
||||
t.tm_hour = (hhmm >> 8) & 0x1f;
|
||||
|
||||
t.tm_mday = yymmdd & 0x1f;
|
||||
t.tm_mon = ((yymmdd >> 5) & 0x0f) - 1;
|
||||
t.tm_year = (yymmdd) >> 9;
|
||||
|
||||
if (t.tm_year <= 39) t.tm_year += 100;
|
||||
|
||||
return mktime(&t);
|
||||
}
|
||||
|
||||
|
||||
|
||||
FileEntry::FileEntry()
|
||||
{
|
||||
bzero(this, sizeof(FileEntry));
|
||||
}
|
||||
FileEntry::FileEntry(const FileEntry& f)
|
||||
{
|
||||
*this = f;
|
||||
}
|
||||
FileEntry::FileEntry(const FileEntry *f)
|
||||
{
|
||||
if (f) *this = *f;
|
||||
else bzero(this, sizeof(FileEntry));
|
||||
}
|
||||
|
||||
FileEntry::FileEntry(const void *data)
|
||||
{
|
||||
const uint8_t *cp = (const uint8_t *)data;
|
||||
|
||||
address = 0;
|
||||
|
||||
storage_type = cp[0x00] >> 4;
|
||||
name_length = cp[0x00] & 0x0f;
|
||||
|
||||
memcpy(file_name, &cp[0x01], name_length);
|
||||
file_name[name_length] = 0;
|
||||
|
||||
file_type = cp[0x10];
|
||||
|
||||
key_pointer = load16(&cp[0x11]);
|
||||
|
||||
blocks_used = load16(&cp[0x13]);
|
||||
|
||||
eof = load24(&cp[0x15]);
|
||||
|
||||
creation = timeToUnix(load16(&cp[0x18]), load16(&cp[0x1a]));
|
||||
|
||||
//version = cp[0x1c];
|
||||
//min_version = cp[0x1d];
|
||||
|
||||
unsigned xcase = load16(&cp[0x1c]);
|
||||
if (xcase & 0x8000)
|
||||
{
|
||||
// gsos technote #8
|
||||
unsigned mask = 0x4000;
|
||||
for (unsigned i = 0; i < name_length; i++)
|
||||
{
|
||||
if (xcase & mask) file_name[i] = tolower(file_name[i]);
|
||||
mask = mask >> 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
access = cp[0x1e];
|
||||
|
||||
|
||||
aux_type = load16(&cp[0x1f]);
|
||||
|
||||
last_mod = timeToUnix(load16(&cp[0x21]), load16(&cp[0x23]));
|
||||
|
||||
header_pointer = load16(&cp[0x25]);
|
||||
}
|
||||
|
||||
|
||||
ExtendedEntry::ExtendedEntry()
|
||||
{
|
||||
bzero(this, sizeof(ExtendedEntry));
|
||||
}
|
||||
|
||||
ExtendedEntry::ExtendedEntry(const void *data)
|
||||
{
|
||||
const uint8_t *cp = (const uint8_t *)data;
|
||||
|
||||
//prodos technote #25.
|
||||
// offset 0 - mini entry for data fork
|
||||
|
||||
dataFork.storage_type = cp[0x00] & 0x0f;
|
||||
dataFork.key_block = load16(&cp[0x01]);
|
||||
dataFork.blocks_used = load16(&cp[0x03]);
|
||||
dataFork.eof = load24(&cp[0x05]);
|
||||
|
||||
// offset 256 - mini entry for resource fork.
|
||||
|
||||
resourceFork.storage_type = cp[256 + 0x00] & 0x0f;
|
||||
resourceFork.key_block = load16(&cp[256 + 0x01]);
|
||||
resourceFork.blocks_used = load16(&cp[256 + 0x03]);
|
||||
resourceFork.eof = load24(&cp[256 + 0x05]);
|
||||
|
||||
// xFInfo may be missing.
|
||||
bzero(FInfo, sizeof(FInfo));
|
||||
bzero(xFInfo, sizeof(xFInfo));
|
||||
|
||||
// size must be 18.
|
||||
unsigned size;
|
||||
unsigned entry;
|
||||
|
||||
for (unsigned i = 0; i < 2; i++)
|
||||
{
|
||||
unsigned ptr = i == 0 ? 8 : 26;
|
||||
size = cp[ptr];
|
||||
if (size != 18) continue;
|
||||
entry = cp[ptr + 1];
|
||||
switch(entry)
|
||||
{
|
||||
case 1:
|
||||
memcpy(FInfo, &cp[ptr + 2], 16);
|
||||
break;
|
||||
case 2:
|
||||
memcpy(xFInfo, &cp[ptr + 2], 16);
|
||||
break;
|
||||
}
|
||||
}
|
||||
//
|
||||
}
|
||||
|
||||
|
||||
|
||||
VolumeEntry::VolumeEntry()
|
||||
{
|
||||
bzero(this, sizeof(VolumeEntry));
|
||||
}
|
||||
|
||||
|
||||
VolumeEntry::VolumeEntry(const void *data)
|
||||
{
|
||||
const uint8_t *cp = (const uint8_t *)data;
|
||||
|
||||
//prev_block = load16(&cp[0x00]);
|
||||
//next_block = load16(&cp[0x02]);
|
||||
|
||||
storage_type = cp[0x00] >> 4;
|
||||
name_length = cp[0x00] & 0x0f;
|
||||
|
||||
memcpy(volume_name, &cp[0x01], name_length);
|
||||
volume_name[name_length] = 0;
|
||||
|
||||
// 0x14--0x1b reserved
|
||||
|
||||
//creation = timeToUnix(load16(&cp[0x18]), load16(&cp[0x1a]));
|
||||
|
||||
//version = cp[0x1c];
|
||||
//min_version = cp[0x1d];
|
||||
|
||||
unsigned xcase = load16(&cp[0x16]);
|
||||
if (xcase & 0x8000)
|
||||
{
|
||||
// gsos technote #8
|
||||
unsigned mask = 0x4000;
|
||||
for (unsigned i = 0; i < name_length; i++)
|
||||
{
|
||||
if (xcase & mask) volume_name[i] = tolower(volume_name[i]);
|
||||
mask = mask >> 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
access = cp[0x1e];
|
||||
|
||||
entry_length = cp[0x1f];
|
||||
|
||||
entries_per_block = cp[0x20];
|
||||
|
||||
file_count = load16(&cp[0x21]);
|
||||
|
||||
bit_map_pointer = load16(&cp[0x23]);
|
||||
|
||||
total_blocks = load16(&cp[0x25]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
SubdirEntry::SubdirEntry()
|
||||
{
|
||||
bzero(this, sizeof(SubdirEntry));
|
||||
}
|
||||
|
||||
|
||||
SubdirEntry::SubdirEntry(const void *data)
|
||||
{
|
||||
const uint8_t *cp = (const uint8_t *)data;
|
||||
|
||||
|
||||
//prev_block = load16(&cp[0x00]);
|
||||
//next_block = load16(&cp[0x02]);
|
||||
|
||||
storage_type = cp[0x00] >> 4;
|
||||
name_length = cp[0x00] & 0x0f;
|
||||
|
||||
memcpy(subdir_name, &cp[0x01], name_length);
|
||||
subdir_name[name_length] = 0;
|
||||
|
||||
// 0x14 should be $14.
|
||||
|
||||
// 0x145-0x1b reserved
|
||||
|
||||
creation = timeToUnix(load16(&cp[0x18]), load16(&cp[0x1a]));
|
||||
|
||||
//version = cp[0x1c];
|
||||
//min_version = cp[0x1d];
|
||||
/*
|
||||
unsigned xcase = load16(&cp[0x1c]);
|
||||
if (xcase & 0x8000)
|
||||
{
|
||||
// gsos technote #8
|
||||
unsigned mask = 0x4000;
|
||||
for (unsigned i = 0; i < name_length; i++)
|
||||
{
|
||||
if (xcase & mask) subdir_name[i] = tolower(subdir_name[i]);
|
||||
mask = mask >> 1;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
access = cp[0x1e];
|
||||
|
||||
entry_length = cp[0x1f];
|
||||
|
||||
entries_per_block = cp[0x20];
|
||||
|
||||
file_count = load16(&cp[0x21]);
|
||||
|
||||
parent_pointer = load16(&cp[0x23]);
|
||||
|
||||
parent_entry = cp[0x25];
|
||||
|
||||
parent_entry_length = cp[0x26];
|
||||
}
|
144
File.h
Normal file
144
File.h
Normal file
@ -0,0 +1,144 @@
|
||||
/*
|
||||
* File.h
|
||||
* ProFUSE
|
||||
*
|
||||
* Created by Kelvin Sherlock on 12/18/08.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __FILE_H__
|
||||
#define __FILE_H__
|
||||
|
||||
#include <time.h>
|
||||
#include <stdint.h>
|
||||
|
||||
enum {
|
||||
DELETED_FILE = 0,
|
||||
SEEDLING_FILE = 1,
|
||||
SAPLING_FILE = 2,
|
||||
TREE_FILE = 3,
|
||||
PASCAL_FILE = 4,
|
||||
EXTENDED_FILE = 5,
|
||||
DIRECTORY_FILE = 0x0d,
|
||||
SUBDIR_HEADER = 0x0e,
|
||||
VOLUME_HEADER = 0x0f
|
||||
};
|
||||
|
||||
|
||||
enum {
|
||||
FILE_ENTRY_SIZE = 0x27,
|
||||
};
|
||||
|
||||
|
||||
enum {
|
||||
ACCESS_DESTROY = 0x80,
|
||||
ACCESS_RENAME = 0x40,
|
||||
ACCESS_MODIFIED = 0x20,
|
||||
ACCESS_WRITE = 0x02,
|
||||
ACCRESS_READ = 0x01
|
||||
};
|
||||
|
||||
|
||||
class FileEntry {
|
||||
public:
|
||||
|
||||
FileEntry();
|
||||
FileEntry(const FileEntry& f);
|
||||
FileEntry(const FileEntry *f);
|
||||
|
||||
FileEntry(const void *data);
|
||||
|
||||
unsigned storage_type;
|
||||
unsigned name_length;
|
||||
char file_name[15 + 1];
|
||||
unsigned file_type;
|
||||
unsigned key_pointer;
|
||||
unsigned blocks_used;
|
||||
uint32_t eof;
|
||||
time_t creation;
|
||||
//unsigned version;
|
||||
//unsigned min_version;
|
||||
unsigned access;
|
||||
unsigned aux_type;
|
||||
time_t last_mod;
|
||||
unsigned header_pointer;
|
||||
|
||||
uint32_t address;
|
||||
};
|
||||
|
||||
#if 0
|
||||
class BlockList {
|
||||
public:
|
||||
unsigned prev_block;
|
||||
unsigned next_block;
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
struct MiniEntry {
|
||||
|
||||
unsigned storage_type;
|
||||
unsigned key_block;
|
||||
unsigned blocks_used;
|
||||
uint32_t eof;
|
||||
|
||||
};
|
||||
|
||||
class ExtendedEntry {
|
||||
public:
|
||||
ExtendedEntry();
|
||||
ExtendedEntry(const void *data);
|
||||
|
||||
MiniEntry dataFork;
|
||||
MiniEntry resourceFork;
|
||||
|
||||
uint8_t FInfo[16];
|
||||
uint8_t xFInfo[16];
|
||||
};
|
||||
|
||||
|
||||
class VolumeEntry {
|
||||
public:
|
||||
VolumeEntry();
|
||||
VolumeEntry(const void *data);
|
||||
|
||||
unsigned storage_type;
|
||||
unsigned name_length;
|
||||
char volume_name[15+1];
|
||||
time_t creation;
|
||||
//unsigned version;
|
||||
//unsigned min_version;
|
||||
unsigned access;
|
||||
unsigned entry_length;
|
||||
unsigned entries_per_block;
|
||||
unsigned file_count;
|
||||
unsigned bit_map_pointer;
|
||||
unsigned total_blocks;
|
||||
|
||||
friend class DirIter;
|
||||
|
||||
};
|
||||
|
||||
class SubdirEntry {
|
||||
public:
|
||||
|
||||
SubdirEntry();
|
||||
SubdirEntry(const void *data);
|
||||
|
||||
unsigned storage_type;
|
||||
unsigned name_length;
|
||||
char subdir_name[15+1];
|
||||
time_t creation;
|
||||
//unsigned version;
|
||||
//unsigned min_version;
|
||||
unsigned access;
|
||||
unsigned entry_length;
|
||||
unsigned entries_per_block;
|
||||
unsigned file_count;
|
||||
unsigned parent_pointer;
|
||||
unsigned parent_entry;
|
||||
unsigned parent_entry_length;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
33
common.h
Normal file
33
common.h
Normal file
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* common.h
|
||||
* ProFUSE
|
||||
*
|
||||
* Created by Kelvin Sherlock on 12/20/08.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __COMMON_H__
|
||||
#define __COMMON_H__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define BLOCK_SIZE 512
|
||||
|
||||
inline unsigned load16(const uint8_t *cp)
|
||||
{
|
||||
return (cp[1] << 8 ) | cp[0];
|
||||
}
|
||||
|
||||
inline unsigned load24(const uint8_t *cp)
|
||||
{
|
||||
return (cp[2] << 16 ) | (cp[1] << 8) | (cp[0]);
|
||||
}
|
||||
|
||||
inline unsigned load32(const uint8_t *cp)
|
||||
{
|
||||
return (cp[3] << 24) | (cp[2] << 16 ) | (cp[1] << 8) | (cp[0]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif
|
967
main.cpp
Normal file
967
main.cpp
Normal file
@ -0,0 +1,967 @@
|
||||
/*
|
||||
* main.cpp
|
||||
* ProFUSE
|
||||
*
|
||||
* Created by Kelvin Sherlock on 12/24/08.
|
||||
*
|
||||
*/
|
||||
/*
|
||||
|
||||
#define __FreeBSD__ 10
|
||||
#define _FILE_OFFSET_BITS 64
|
||||
#define __DARWIN_64_BIT_INO_T 1
|
||||
#define _REENTRANT
|
||||
#define _POSIX_C_SOURCE 200112L
|
||||
*/
|
||||
#define FUSE_USE_VERSION 26
|
||||
|
||||
|
||||
|
||||
#include <fuse_lowlevel.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
|
||||
#include "common.h"
|
||||
#include "Disk.h"
|
||||
#include "File.h"
|
||||
|
||||
|
||||
using std::vector;
|
||||
using std::string;
|
||||
|
||||
Disk *disk = NULL;
|
||||
char *dfile = NULL;
|
||||
|
||||
|
||||
|
||||
#undef ERROR
|
||||
#define ERROR(cond,errno) if ( (cond) ){ fuse_reply_err(req, errno); return; }
|
||||
|
||||
|
||||
|
||||
static bool validProdosName(const char *name)
|
||||
{
|
||||
// OS X looks for hidden files that don't exist (and aren't legal prodos names)
|
||||
// most are not legal prodos names, so this filters them out easily.
|
||||
|
||||
// [A-Za-z][0-9A-Za-z.]{0,14}
|
||||
|
||||
if (!isalpha(*name)) return false;
|
||||
|
||||
unsigned i;
|
||||
for(i = 1; name[i]; i++)
|
||||
{
|
||||
char c = name[i];
|
||||
if (c == '.' || isalnum(c)) continue;
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
return i < 16;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark XAttribute Functions
|
||||
|
||||
|
||||
static void xattr_filetype(FileEntry& e, fuse_req_t req, size_t size, off_t off)
|
||||
{
|
||||
uint8_t attr = e.file_type;
|
||||
unsigned attr_size = 1;
|
||||
|
||||
if (size == 0)
|
||||
{
|
||||
fuse_reply_xattr(req, attr_size);
|
||||
return;
|
||||
}
|
||||
|
||||
ERROR (size < attr_size, ERANGE)
|
||||
|
||||
// consider position here?
|
||||
fuse_reply_buf(req, (char *)&attr, attr_size);
|
||||
}
|
||||
|
||||
|
||||
static void xattr_auxtype(FileEntry& e, fuse_req_t req, size_t size, off_t off)
|
||||
{
|
||||
uint8_t attr[2];
|
||||
unsigned attr_size = 2;
|
||||
|
||||
attr[0] = e.aux_type & 0xff;
|
||||
attr[1] = (e.aux_type >> 8) & 0xff;
|
||||
|
||||
if (size == 0)
|
||||
{
|
||||
fuse_reply_xattr(req, attr_size);
|
||||
return;
|
||||
}
|
||||
|
||||
ERROR (size < attr_size, ERANGE)
|
||||
|
||||
// consider position here?
|
||||
fuse_reply_buf(req, (char *)&attr, attr_size);
|
||||
}
|
||||
|
||||
static void xattr_textencoding(FileEntry& e, fuse_req_t req, size_t size, off_t off)
|
||||
{
|
||||
// TODO -- ascii text encoding for ascii files?
|
||||
const char attr[] = "MACINTOSH;0";
|
||||
unsigned attr_size = sizeof(attr) - 1;
|
||||
|
||||
// currently only valid for Teach Files.
|
||||
if (e.file_type != 0x50 || e.aux_type != 0x5445)
|
||||
{
|
||||
ERROR(true, ENOENT)
|
||||
}
|
||||
|
||||
if (size == 0)
|
||||
{
|
||||
fuse_reply_xattr(req, attr_size);
|
||||
return;
|
||||
}
|
||||
|
||||
ERROR (size < attr_size, ERANGE)
|
||||
|
||||
// consider position here?
|
||||
fuse_reply_buf(req, (char *)&attr, attr_size);
|
||||
}
|
||||
|
||||
static void xattr_rfork(FileEntry& e, fuse_req_t req, size_t size, off_t off)
|
||||
{
|
||||
int ok;
|
||||
unsigned level;
|
||||
|
||||
ERROR (e.storage_type != EXTENDED_FILE, ENOENT)
|
||||
|
||||
ok = disk->Normalize(e, 1);
|
||||
ERROR(ok < 0, EIO)
|
||||
|
||||
|
||||
switch(e.storage_type)
|
||||
{
|
||||
case SEEDLING_FILE:
|
||||
level = 0;
|
||||
break;
|
||||
case SAPLING_FILE:
|
||||
level = 1;
|
||||
break;
|
||||
case TREE_FILE:
|
||||
level = 2;
|
||||
break;
|
||||
default:
|
||||
ERROR(true, EIO)
|
||||
}
|
||||
|
||||
if (size == 0)
|
||||
{
|
||||
fuse_reply_xattr(req, e.eof);
|
||||
return;
|
||||
}
|
||||
|
||||
size = std::min((uint32_t)(size + off), e.eof);
|
||||
|
||||
unsigned blocks = (size + (off & 0x1ff) + BLOCK_SIZE - 1) >> 9;
|
||||
uint8_t *buffer = new uint8_t[blocks << 9];
|
||||
|
||||
fprintf(stderr, "ReadIndex(%x, buffer, %x, %x, %x)\n", e.key_pointer, level, (int)off, (int)blocks);
|
||||
|
||||
ok = disk->ReadIndex(e.key_pointer, buffer, level, off, blocks);
|
||||
|
||||
if (ok < 0)
|
||||
{
|
||||
fuse_reply_err(req, EIO);
|
||||
}
|
||||
else
|
||||
{
|
||||
fuse_reply_buf(req, (char *)buffer + (off & 0x1ff), size);
|
||||
}
|
||||
delete []buffer;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Finder info.
|
||||
static void xattr_finfo(FileEntry& e, fuse_req_t req, size_t size, off_t off)
|
||||
{
|
||||
int ok;
|
||||
ExtendedEntry ee;
|
||||
|
||||
uint8_t attr[32];
|
||||
unsigned attr_size = 32;
|
||||
|
||||
ERROR (e.storage_type != EXTENDED_FILE, ENOENT)
|
||||
|
||||
ok = disk->Normalize(e, 1, &ee);
|
||||
ERROR(ok < 0, EIO)
|
||||
|
||||
// sanity check
|
||||
switch(e.storage_type)
|
||||
{
|
||||
case SEEDLING_FILE:
|
||||
case SAPLING_FILE:
|
||||
case TREE_FILE:
|
||||
break;
|
||||
default:
|
||||
ERROR(true, EIO)
|
||||
}
|
||||
|
||||
if (size == 0)
|
||||
{
|
||||
fuse_reply_xattr(req, attr_size);
|
||||
return;
|
||||
}
|
||||
|
||||
ERROR (size < attr_size, ERANGE)
|
||||
|
||||
memcpy(attr, ee.FInfo, 16);
|
||||
memcpy(attr + 16, ee.xFInfo, 16);
|
||||
|
||||
fuse_reply_buf(req, (char *)attr, attr_size);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void prodos_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size)
|
||||
{
|
||||
// list of supported attributes.
|
||||
//
|
||||
#define NO_ATTR() \
|
||||
{ \
|
||||
if (size) fuse_reply_buf(req, NULL, 0); \
|
||||
else fuse_reply_xattr(req, 0); \
|
||||
return; \
|
||||
}
|
||||
|
||||
fprintf(stderr, "listxattr %u\n", ino);
|
||||
|
||||
uint8_t buffer[BLOCK_SIZE];
|
||||
int ok;
|
||||
unsigned attr_size;
|
||||
string attr;
|
||||
|
||||
|
||||
|
||||
if(ino == 1)
|
||||
NO_ATTR()
|
||||
|
||||
ok = disk->Read(ino >> 9, buffer);
|
||||
|
||||
ERROR(ok < 0, EIO)
|
||||
|
||||
|
||||
FileEntry e(buffer + (ino & 0x1ff));
|
||||
|
||||
|
||||
attr += "prodos.FileType";
|
||||
attr.append(1, 0);
|
||||
|
||||
attr += "prodos.AuxType";
|
||||
attr.append(1, 0);
|
||||
|
||||
switch(e.storage_type)
|
||||
{
|
||||
case EXTENDED_FILE:
|
||||
{
|
||||
// TODO -- pretend there's no resource fork if resource fork eof == 0 ?
|
||||
//
|
||||
//ok = disk->Normalize(e, 1);
|
||||
//ERROR(ok < 0, EIO)
|
||||
|
||||
attr += "prodos.ResourceFork";
|
||||
attr.append(1, 0);
|
||||
|
||||
attr += "com.apple.FinderInfo";
|
||||
attr.append(1, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
case SEEDLING_FILE:
|
||||
case SAPLING_FILE:
|
||||
case TREE_FILE:
|
||||
break;
|
||||
|
||||
case DIRECTORY_FILE:
|
||||
NO_ATTR()
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
NO_ATTR()
|
||||
break;
|
||||
}
|
||||
|
||||
if (e.file_type == 0x50 && e.aux_type == 0x5445) // teach text
|
||||
{
|
||||
attr += "com.apple.TextEncoding";
|
||||
attr.append(1, 0);
|
||||
}
|
||||
|
||||
attr_size = attr.length();
|
||||
|
||||
fprintf(stderr, "%d %s\n", attr_size, attr.c_str());
|
||||
|
||||
if (size == 0)
|
||||
{
|
||||
fuse_reply_xattr(req, attr_size);
|
||||
return;
|
||||
}
|
||||
|
||||
if (size < attr_size)
|
||||
{
|
||||
fuse_reply_err(req, ERANGE);
|
||||
return;
|
||||
}
|
||||
|
||||
fuse_reply_buf(req, attr.data(), attr_size);
|
||||
return;
|
||||
}
|
||||
|
||||
// position not needed/valid for linux.
|
||||
// position ignored, for now.
|
||||
static void prodos_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name, size_t size, uint32_t off)
|
||||
{
|
||||
|
||||
fprintf(stderr, "getxattr: %u %s %u %u \n", ino, name, (int)size, (int)off);
|
||||
|
||||
uint8_t buffer[BLOCK_SIZE];
|
||||
|
||||
|
||||
ERROR(ino == 1, ENOENT) // finder can't handle EISDIR.
|
||||
|
||||
int ok = disk->Read(ino >> 9, buffer);
|
||||
|
||||
ERROR(ok < 0, EIO)
|
||||
|
||||
|
||||
FileEntry e(buffer + (ino & 0x1ff));
|
||||
|
||||
switch(e.storage_type)
|
||||
{
|
||||
case SEEDLING_FILE:
|
||||
case SAPLING_FILE:
|
||||
case TREE_FILE:
|
||||
case EXTENDED_FILE:
|
||||
break;
|
||||
case DIRECTORY_FILE:
|
||||
ERROR(true, ENOENT) // Finder can't handle EISDIR.
|
||||
default:
|
||||
ERROR(true, ENOENT);
|
||||
}
|
||||
|
||||
if (strcmp("prodos.FileType", name) == 0)
|
||||
{
|
||||
xattr_filetype(e, req, size, off);
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcmp("prodos.AuxType", name) == 0)
|
||||
{
|
||||
xattr_auxtype(e, req, size, off);
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcmp("com.apple.TextEncoding", name) == 0)
|
||||
{
|
||||
xattr_textencoding(e, req, size, off);
|
||||
return;
|
||||
}
|
||||
|
||||
if ( (e.storage_type == EXTENDED_FILE) && (strcmp("prodos.ResourceFork", name) == 0))
|
||||
{
|
||||
xattr_rfork(e, req, size, off);
|
||||
return;
|
||||
}
|
||||
|
||||
if ( (e.storage_type == EXTENDED_FILE) && (strcmp("com.apple.FinderInfo", name) == 0))
|
||||
{
|
||||
xattr_finfo(e, req, size, off);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
fuse_reply_err(req, ENOENT);
|
||||
|
||||
}
|
||||
|
||||
#pragma mark Directory Functions
|
||||
|
||||
/*
|
||||
* when the directory is opened, we load the volume/directory and store the FileEntry vector into
|
||||
* fi->fh.
|
||||
*
|
||||
*/
|
||||
static void prodos_opendir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
|
||||
{
|
||||
fprintf(stderr, "opendir: %u\n", ino);
|
||||
// verify it's a directory/volume here?
|
||||
|
||||
|
||||
uint8_t buffer[BLOCK_SIZE];
|
||||
vector<FileEntry> files;
|
||||
bool ok;
|
||||
|
||||
|
||||
ok = disk->Read(ino == 1 ? 2 : ino >> 9, buffer);
|
||||
ERROR(ok < 0, EIO)
|
||||
|
||||
|
||||
if (ino == 1)
|
||||
{
|
||||
VolumeEntry v(buffer + 0x04);
|
||||
|
||||
ok = disk->ReadVolume(&v, &files);
|
||||
|
||||
ERROR(ok < 0, EIO)
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
FileEntry e(buffer + (ino & 0x1ff));
|
||||
|
||||
ERROR(e.storage_type != DIRECTORY_FILE, ENOTDIR)
|
||||
|
||||
ok = disk->ReadDirectory(e.key_pointer, NULL, &files);
|
||||
|
||||
ERROR(ok < 0, EIO);
|
||||
}
|
||||
|
||||
// copy the vector contents to a vector *.
|
||||
vector<FileEntry> *fp = new vector<FileEntry>();
|
||||
files.swap(*fp);
|
||||
|
||||
fi->fh = (uint64_t)fp;
|
||||
fuse_reply_open(req, fi);
|
||||
}
|
||||
|
||||
static void prodos_releasedir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
|
||||
{
|
||||
fprintf(stderr,"releasedir: %d\n", ino);
|
||||
vector<FileEntry> *files = (vector<FileEntry> *)fi->fh;
|
||||
|
||||
if (files) delete files;
|
||||
|
||||
fuse_reply_err(req, 0);
|
||||
}
|
||||
static void prodos_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi)
|
||||
{
|
||||
vector<FileEntry> *files = (vector<FileEntry> *)fi->fh;
|
||||
struct stat st;
|
||||
|
||||
fprintf(stderr, "readdir %u %u %u\n", ino, size, off);
|
||||
|
||||
// TODO -- add "." and ".." entries...
|
||||
|
||||
|
||||
// if the offset >= number of entries, get out.
|
||||
if (!files || files->size() <= off)
|
||||
{
|
||||
fprintf(stderr, "fuse_reply_buf(req, NULL, 0)\n");
|
||||
fuse_reply_buf(req, NULL, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// now some dirent info...
|
||||
|
||||
bzero(&st, sizeof(st));
|
||||
// only mode and ino are used.
|
||||
|
||||
char *buffer = new char[size];
|
||||
|
||||
unsigned count = files->size();
|
||||
unsigned current_size = 0;
|
||||
for (unsigned i = off; i < count; ++i)
|
||||
{
|
||||
FileEntry &f = (*files)[i];
|
||||
|
||||
st.st_mode = f.storage_type == DIRECTORY_FILE ? S_IFDIR | 0555 : S_IFREG | 0444;
|
||||
st.st_ino = f.address;
|
||||
|
||||
unsigned entry_size = fuse_add_direntry(req, NULL, 0, f.file_name, NULL, 0);
|
||||
if (entry_size + current_size >= size) break;
|
||||
|
||||
|
||||
fuse_add_direntry(req, (char *)buffer + current_size, size, f.file_name, &st, i + 1);
|
||||
current_size += entry_size;
|
||||
|
||||
}
|
||||
|
||||
fuse_reply_buf(req, buffer, current_size);
|
||||
delete []buffer;
|
||||
}
|
||||
|
||||
#pragma mark Stat Functions
|
||||
|
||||
int prodos_stat(FileEntry& e, struct stat *st)
|
||||
{
|
||||
uint8_t buffer[BLOCK_SIZE];
|
||||
int ok;
|
||||
|
||||
|
||||
if (e.storage_type == EXTENDED_FILE)
|
||||
{
|
||||
ok = disk->Normalize(e, 0);
|
||||
if (ok < 0) return ok;
|
||||
}
|
||||
|
||||
st->st_blksize = BLOCK_SIZE;
|
||||
|
||||
st->st_ctime = e.creation;
|
||||
#ifdef STAT_BIRTHTIME
|
||||
//st->st_birthtime = e.creation;
|
||||
#endif
|
||||
|
||||
st->st_mtime = e.last_mod;
|
||||
st->st_atime = e.last_mod;
|
||||
|
||||
|
||||
st->st_nlink = 1;
|
||||
st->st_mode = 0444 | S_IFREG;
|
||||
st->st_size = e.eof;
|
||||
|
||||
|
||||
if (e.storage_type == DIRECTORY_FILE)
|
||||
{
|
||||
ok = disk->Read(e.key_pointer, buffer);
|
||||
if (ok < 0) return -1;
|
||||
|
||||
SubdirEntry se(buffer + 0x04);
|
||||
|
||||
if (se.storage_type != SUBDIR_HEADER) return -1;
|
||||
|
||||
st->st_mode = S_IFDIR | 0555;
|
||||
st->st_size = BLOCK_SIZE;
|
||||
st->st_nlink = se.file_count + 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
switch(e.storage_type)
|
||||
{
|
||||
case SEEDLING_FILE:
|
||||
case SAPLING_FILE:
|
||||
case TREE_FILE:
|
||||
//case PASCAL_FILE:
|
||||
break;
|
||||
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int prodos_stat(const VolumeEntry &v, struct stat *st)
|
||||
{
|
||||
|
||||
if (v.storage_type != VOLUME_HEADER) return -1;
|
||||
|
||||
st->st_mode = S_IFDIR | 0555;
|
||||
st->st_ctime = v.creation;
|
||||
//st->st_birthtime = v.creation;
|
||||
st->st_mtime = v.creation;
|
||||
st->st_atime = v.creation;
|
||||
|
||||
st->st_nlink = v.file_count + 1;
|
||||
st->st_size = BLOCK_SIZE;
|
||||
st->st_blksize = BLOCK_SIZE;
|
||||
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static void prodos_getattr(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
|
||||
{
|
||||
uint8_t buffer[BLOCK_SIZE];
|
||||
struct stat st;
|
||||
int ok;
|
||||
|
||||
fprintf(stderr, "get_attr %u\n", ino);
|
||||
|
||||
bzero(&st, sizeof(st));
|
||||
|
||||
/*
|
||||
* ino 1 is the volume header. Others are pointers.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
ok = disk->Read(ino == 1 ? 2 : ino >> 9, buffer);
|
||||
ERROR(ok < 0, EIO)
|
||||
|
||||
// ino 1 is the volume header.
|
||||
if (ino == 1)
|
||||
{
|
||||
VolumeEntry v(buffer + 0x04);
|
||||
ok = prodos_stat(v, &st);
|
||||
ERROR(ok < 0, EIO);
|
||||
|
||||
st.st_ino = ino;
|
||||
|
||||
fuse_reply_attr(req, &st, 0.0);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
|
||||
FileEntry e(buffer + (ino & 0x1ff));
|
||||
ok = prodos_stat(e, &st);
|
||||
|
||||
ERROR(ok < 0, EIO);
|
||||
|
||||
st.st_ino = ino;
|
||||
|
||||
fuse_reply_attr(req, &st, 0.0); //
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// TODO -- add Disk::Lookup support so we don't have to parse the entire dir header.
|
||||
static void prodos_lookup(fuse_req_t req, fuse_ino_t parent, const char *name)
|
||||
{
|
||||
uint8_t buffer[BLOCK_SIZE];
|
||||
struct fuse_entry_param entry;
|
||||
int ok;
|
||||
vector<FileEntry> files;
|
||||
|
||||
|
||||
fprintf(stderr, "lookup: %d %s\n", parent, name);
|
||||
|
||||
ERROR(!validProdosName(name), ENOENT)
|
||||
|
||||
ok = disk->Read(parent == 1 ? 2 : parent >> 9, buffer);
|
||||
ERROR(ok < 0, EIO)
|
||||
|
||||
bzero(&entry, sizeof(entry));
|
||||
|
||||
entry.attr_timeout = 0.0;
|
||||
entry.entry_timeout = 0.0;
|
||||
|
||||
// get the file list
|
||||
// TODO -- Disk::look-up-one-file
|
||||
if (parent == 1)
|
||||
{
|
||||
VolumeEntry v;
|
||||
ok = disk->ReadVolume(&v, &files);
|
||||
ERROR(ok < 0, EIO)
|
||||
}
|
||||
else
|
||||
{
|
||||
FileEntry e(buffer + (parent & 0x1ff));
|
||||
ERROR(e.storage_type != DIRECTORY_FILE, ENOENT);
|
||||
|
||||
ok = disk->ReadDirectory(e.key_pointer, NULL, &files);
|
||||
ERROR(ok < 0, EIO)
|
||||
}
|
||||
// ok, now go through the file list and look for a (case insensitive) match.
|
||||
|
||||
|
||||
ok = -1;
|
||||
unsigned name_length = strlen(name);
|
||||
|
||||
for(vector<FileEntry>::iterator iter = files.begin(); iter != files.end(); iter++)
|
||||
{
|
||||
FileEntry& f = *iter;
|
||||
if ( (f.name_length == name_length) && (strcasecmp(name, f.file_name) == 0))
|
||||
{
|
||||
ok = prodos_stat(f, &entry.attr);
|
||||
fprintf(stderr, "stat %s %x (%x %x) %d\n", f.file_name, f.address, f.address >> 9, f.address & 0x1ff, ok);
|
||||
entry.ino = f.address;
|
||||
entry.attr.st_ino = f.address;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ERROR(ok < 0, ENOENT);
|
||||
|
||||
fprintf(stderr, "file found!\n");
|
||||
|
||||
fuse_reply_entry(req, &entry);
|
||||
}
|
||||
|
||||
|
||||
#pragma mark Read Functions
|
||||
static void prodos_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
|
||||
{
|
||||
fprintf(stderr, "open: %u\n", ino);
|
||||
|
||||
|
||||
uint8_t buffer[BLOCK_SIZE];
|
||||
int ok;
|
||||
|
||||
FileEntry *e = NULL;
|
||||
|
||||
ERROR(ino == 1, EISDIR)
|
||||
|
||||
ok = disk->Read(ino >> 9, buffer);
|
||||
ERROR(ok < 0, EIO)
|
||||
|
||||
e = new FileEntry(buffer + (ino & 0x1ff));
|
||||
|
||||
if (e->storage_type == EXTENDED_FILE)
|
||||
{
|
||||
ok = disk->Normalize(*e, 0);
|
||||
|
||||
if (ok < 0)
|
||||
{
|
||||
delete e;
|
||||
ERROR(true, EIO)
|
||||
}
|
||||
}
|
||||
|
||||
// EXTENDED_FILE already handled (it would be an error here.)
|
||||
switch(e->storage_type)
|
||||
{
|
||||
case SEEDLING_FILE:
|
||||
case SAPLING_FILE:
|
||||
case TREE_FILE:
|
||||
break;
|
||||
//case PASCAL_FILE: //?
|
||||
case DIRECTORY_FILE:
|
||||
delete e;
|
||||
ERROR(true, EISDIR)
|
||||
break;
|
||||
default:
|
||||
ERROR(true, EIO)
|
||||
}
|
||||
|
||||
if (fi->flags != O_RDONLY)
|
||||
{
|
||||
delete e;
|
||||
ERROR(true, EACCES);
|
||||
}
|
||||
fi->fh = (uint64_t)e;
|
||||
|
||||
fuse_reply_open(req, fi);
|
||||
}
|
||||
|
||||
static void prodos_release(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
|
||||
{
|
||||
fprintf(stderr, "release: %d\n", ino);
|
||||
|
||||
FileEntry *e = (FileEntry *)fi->fh;
|
||||
|
||||
if (e) delete e;
|
||||
|
||||
fuse_reply_err(req, 0);
|
||||
|
||||
}
|
||||
|
||||
static void prodos_read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi)
|
||||
{
|
||||
fprintf(stderr, "read: %u %u %u\n", ino, size, off);
|
||||
|
||||
FileEntry *e = (FileEntry *)fi->fh;
|
||||
|
||||
ERROR(e == NULL, EIO)
|
||||
|
||||
if (off >= e->eof)
|
||||
{
|
||||
fuse_reply_buf(req, NULL, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
unsigned level = 0;
|
||||
switch(e->storage_type)
|
||||
{
|
||||
case TREE_FILE:
|
||||
level = 2;
|
||||
break;
|
||||
case SAPLING_FILE:
|
||||
level = 1;
|
||||
break;
|
||||
case SEEDLING_FILE:
|
||||
level = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
// currently, reading is done on a block basis.
|
||||
// experimentally, fuse reads the entire file
|
||||
// this may not hold for larger files.
|
||||
|
||||
|
||||
// TODO -- error if size + off > eof.
|
||||
|
||||
unsigned blocks = (size + (off & 0x1ff) + BLOCK_SIZE - 1) >> 9;
|
||||
int ok;
|
||||
uint8_t *buffer = new uint8_t[blocks << 9];
|
||||
|
||||
fprintf(stderr, "ReadIndex(%x, buffer, %x, %x, %x)\n", e->key_pointer, level, (int)off, (int)blocks);
|
||||
|
||||
ok = disk->ReadIndex(e->key_pointer, buffer, level, off, blocks);
|
||||
if (ok < 0)
|
||||
{
|
||||
fuse_reply_err(req, EIO);
|
||||
}
|
||||
else
|
||||
{
|
||||
fuse_reply_buf(req, (const char *)buffer + (off & 0x1ff), size);
|
||||
}
|
||||
|
||||
delete []buffer;
|
||||
}
|
||||
|
||||
|
||||
static struct fuse_lowlevel_ops prodos_oper;
|
||||
|
||||
enum {
|
||||
PRODOS_OPT_HELP,
|
||||
PRODOS_OPT_VERSION
|
||||
};
|
||||
|
||||
static struct fuse_opt prodos_opts[] = {
|
||||
FUSE_OPT_KEY("-h", PRODOS_OPT_HELP),
|
||||
FUSE_OPT_KEY("--help", PRODOS_OPT_HELP),
|
||||
FUSE_OPT_KEY("-V", PRODOS_OPT_VERSION),
|
||||
FUSE_OPT_KEY("--version", PRODOS_OPT_VERSION),
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
static void usage()
|
||||
{
|
||||
fprintf(stderr, "profuse [options] disk_image mountpoint\n");
|
||||
}
|
||||
|
||||
static int prodos_opt_proc(void *data, const char *arg, int key, struct fuse_args *outargs)
|
||||
{
|
||||
switch(key)
|
||||
{
|
||||
/*
|
||||
case PRODOS_OPT_HELP:
|
||||
usage();
|
||||
exit(0);
|
||||
break;
|
||||
*/
|
||||
case PRODOS_OPT_VERSION:
|
||||
// TODO
|
||||
exit(1);
|
||||
break;
|
||||
|
||||
case FUSE_OPT_KEY_NONOPT:
|
||||
if (dfile == NULL)
|
||||
{
|
||||
dfile = strdup(arg);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
|
||||
struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
|
||||
struct fuse_chan *ch;
|
||||
char *mountpoint = NULL;
|
||||
int err = -1;
|
||||
|
||||
disk = NULL;
|
||||
|
||||
bzero(&prodos_oper, sizeof(prodos_oper));
|
||||
|
||||
prodos_oper.listxattr = prodos_listxattr;
|
||||
prodos_oper.getxattr = prodos_getxattr;
|
||||
|
||||
prodos_oper.opendir = prodos_opendir;
|
||||
prodos_oper.releasedir = prodos_releasedir;
|
||||
prodos_oper.readdir = prodos_readdir;
|
||||
|
||||
prodos_oper.lookup = prodos_lookup;
|
||||
prodos_oper.getattr = prodos_getattr;
|
||||
|
||||
prodos_oper.open = prodos_open;
|
||||
prodos_oper.release = prodos_release;
|
||||
prodos_oper.read = prodos_read;
|
||||
|
||||
|
||||
// scan the argument list, looking for the name of the disk image.
|
||||
if (fuse_opt_parse(&args, NULL , prodos_opts, prodos_opt_proc) == -1)
|
||||
exit(1);
|
||||
|
||||
if (dfile == NULL || *dfile == 0)
|
||||
{
|
||||
usage();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
// TODO -- check file size, volume header
|
||||
// check for 2mg or disk copy 4.2 header.
|
||||
disk = Disk::OpenFile(dfile);
|
||||
|
||||
if (!disk)
|
||||
{
|
||||
fprintf(stderr, "Unable to mount disk %s\n", dfile);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// tODO -- add blocksize, volume name
|
||||
|
||||
do {
|
||||
|
||||
if (fuse_parse_cmdline(&args, &mountpoint, NULL, NULL) == -1) break;
|
||||
|
||||
|
||||
if (mountpoint == NULL || *mountpoint == 0)
|
||||
{
|
||||
fprintf(stderr, "no mount point\n");
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if ( (ch = fuse_mount(mountpoint, &args)) != NULL)
|
||||
{
|
||||
struct fuse_session *se;
|
||||
|
||||
se = fuse_lowlevel_new(&args, &prodos_oper, sizeof(prodos_oper), NULL);
|
||||
if (se != NULL)
|
||||
{
|
||||
if (fuse_set_signal_handlers(se) != -1)
|
||||
{
|
||||
fuse_session_add_chan(se, ch);
|
||||
err = fuse_session_loop(se);
|
||||
fuse_remove_signal_handlers(se);
|
||||
fuse_session_remove_chan(ch);
|
||||
}
|
||||
|
||||
fuse_session_destroy(se);
|
||||
}
|
||||
fuse_unmount(mountpoint, ch);
|
||||
}
|
||||
|
||||
} while (false);
|
||||
|
||||
fuse_opt_free_args(&args);
|
||||
|
||||
if (disk)
|
||||
{
|
||||
delete disk;
|
||||
disk = NULL;
|
||||
}
|
||||
|
||||
if (dfile) free(dfile);
|
||||
|
||||
return err ? 1 : 0;
|
||||
}
|
79
profuse.1
Normal file
79
profuse.1
Normal file
@ -0,0 +1,79 @@
|
||||
.\"Modified from man(1) of FreeBSD, the NetBSD mdoc.template, and mdoc.samples.
|
||||
.\"See Also:
|
||||
.\"man mdoc.samples for a complete listing of options
|
||||
.\"man mdoc for the short list of editing options
|
||||
.\"/usr/share/misc/mdoc.template
|
||||
.Dd 12/18/08 \" DATE
|
||||
.Dt profuse 1 \" Program name and manual section number
|
||||
.Os Darwin
|
||||
.Sh NAME \" Section Header - required - don't modify
|
||||
.Nm ProFUSE,
|
||||
.\" The following lines are read in generating the apropos(man -k) database. Use only key
|
||||
.\" words here as the database is built based on the words here and in the .ND line.
|
||||
.Nm Other_name_for_same_program(),
|
||||
.Nm Yet another name for the same program.
|
||||
.\" Use .Nm macro to designate other names for the documented program.
|
||||
.Nd This line parsed for whatis database.
|
||||
.Sh SYNOPSIS \" Section Header - required - don't modify
|
||||
.Nm
|
||||
.Op Fl abcd \" [-abcd]
|
||||
.Op Fl a Ar path \" [-a path]
|
||||
.Op Ar file \" [file]
|
||||
.Op Ar \" [file ...]
|
||||
.Ar arg0 \" Underlined argument - use .Ar anywhere to underline
|
||||
arg2 ... \" Arguments
|
||||
.Sh DESCRIPTION \" Section Header - required - don't modify
|
||||
Use the .Nm macro to refer to your program throughout the man page like such:
|
||||
.Nm
|
||||
Underlining is accomplished with the .Ar macro like this:
|
||||
.Ar underlined text .
|
||||
.Pp \" Inserts a space
|
||||
A list of items with descriptions:
|
||||
.Bl -tag -width -indent \" Begins a tagged list
|
||||
.It item a \" Each item preceded by .It macro
|
||||
Description of item a
|
||||
.It item b
|
||||
Description of item b
|
||||
.El \" Ends the list
|
||||
.Pp
|
||||
A list of flags and their descriptions:
|
||||
.Bl -tag -width -indent \" Differs from above in tag removed
|
||||
.It Fl a \"-a flag as a list item
|
||||
Description of -a flag
|
||||
.It Fl b
|
||||
Description of -b flag
|
||||
.El \" Ends the list
|
||||
.Pp
|
||||
.\" .Sh ENVIRONMENT \" May not be needed
|
||||
.\" .Bl -tag -width "ENV_VAR_1" -indent \" ENV_VAR_1 is width of the string ENV_VAR_1
|
||||
.\" .It Ev ENV_VAR_1
|
||||
.\" Description of ENV_VAR_1
|
||||
.\" .It Ev ENV_VAR_2
|
||||
.\" Description of ENV_VAR_2
|
||||
.\" .El
|
||||
.Sh FILES \" File used or created by the topic of the man page
|
||||
.Bl -tag -width "/Users/joeuser/Library/really_long_file_name" -compact
|
||||
.It Pa /usr/share/file_name
|
||||
FILE_1 description
|
||||
.It Pa /Users/joeuser/Library/really_long_file_name
|
||||
FILE_2 description
|
||||
.El \" Ends the list
|
||||
.\" .Sh DIAGNOSTICS \" May not be needed
|
||||
.\" .Bl -diag
|
||||
.\" .It Diagnostic Tag
|
||||
.\" Diagnostic informtion here.
|
||||
.\" .It Diagnostic Tag
|
||||
.\" Diagnostic informtion here.
|
||||
.\" .El
|
||||
.Sh SEE ALSO
|
||||
.\" List links in ascending order by section, alphabetically within a section.
|
||||
.\" Please do not reference files that do not exist without filing a bug report
|
||||
.Xr a 1 ,
|
||||
.Xr b 1 ,
|
||||
.Xr c 1 ,
|
||||
.Xr a 2 ,
|
||||
.Xr b 2 ,
|
||||
.Xr a 3 ,
|
||||
.Xr b 3
|
||||
.\" .Sh BUGS \" Document known, unremedied bugs
|
||||
.\" .Sh HISTORY \" Document history if command behaves in a unique manner
|
1422
profuse.xcodeproj/kelvin.mode1v3
Normal file
1422
profuse.xcodeproj/kelvin.mode1v3
Normal file
File diff suppressed because it is too large
Load Diff
540
profuse.xcodeproj/kelvin.pbxuser
Normal file
540
profuse.xcodeproj/kelvin.pbxuser
Normal file
@ -0,0 +1,540 @@
|
||||
// !$*UTF8*$!
|
||||
{
|
||||
08FB7793FE84155DC02AAC07 /* Project object */ = {
|
||||
activeArchitecturePreference = i386;
|
||||
activeBuildConfigurationName = "Debug Universal";
|
||||
activeExecutable = B60E914A0EFB3612000E4348 /* profuse */;
|
||||
activeTarget = 8DD76F620486A84900D96B5E /* profuse */;
|
||||
addToTargets = (
|
||||
8DD76F620486A84900D96B5E /* profuse */,
|
||||
);
|
||||
breakpoints = (
|
||||
B60E91C10EFD8049000E4348 /* xmain.cpp:129 */,
|
||||
B60E92720EFDA086000E4348 /* File.cpp:88 */,
|
||||
B6D81E5B0EFDE859000219B7 /* xmain.cpp:163 */,
|
||||
B6AE1CFF0F0335FC00D36ADB /* main.cpp:30 */,
|
||||
);
|
||||
codeSenseManager = B60E91510EFB3628000E4348 /* Code sense */;
|
||||
executables = (
|
||||
B60E914A0EFB3612000E4348 /* profuse */,
|
||||
);
|
||||
perUserDictionary = {
|
||||
PBXConfiguration.PBXFileTableDataSource3.PBXFileTableDataSource = {
|
||||
PBXFileTableDataSourceColumnSortingDirectionKey = 1;
|
||||
PBXFileTableDataSourceColumnSortingKey = PBXFileDataSource_Filename_ColumnID;
|
||||
PBXFileTableDataSourceColumnWidthsKey = (
|
||||
20,
|
||||
705,
|
||||
20,
|
||||
48,
|
||||
43,
|
||||
43,
|
||||
20,
|
||||
);
|
||||
PBXFileTableDataSourceColumnsKey = (
|
||||
PBXFileDataSource_FiletypeID,
|
||||
PBXFileDataSource_Filename_ColumnID,
|
||||
PBXFileDataSource_Built_ColumnID,
|
||||
PBXFileDataSource_ObjectSize_ColumnID,
|
||||
PBXFileDataSource_Errors_ColumnID,
|
||||
PBXFileDataSource_Warnings_ColumnID,
|
||||
PBXFileDataSource_Target_ColumnID,
|
||||
);
|
||||
};
|
||||
PBXConfiguration.PBXFileTableDataSource3.XCSCMDataSource = {
|
||||
PBXFileTableDataSourceColumnSortingDirectionKey = "-1";
|
||||
PBXFileTableDataSourceColumnSortingKey = PBXFileDataSource_Filename_ColumnID;
|
||||
PBXFileTableDataSourceColumnWidthsKey = (
|
||||
20,
|
||||
20,
|
||||
681,
|
||||
20,
|
||||
48,
|
||||
43,
|
||||
43,
|
||||
20,
|
||||
);
|
||||
PBXFileTableDataSourceColumnsKey = (
|
||||
PBXFileDataSource_SCM_ColumnID,
|
||||
PBXFileDataSource_FiletypeID,
|
||||
PBXFileDataSource_Filename_ColumnID,
|
||||
PBXFileDataSource_Built_ColumnID,
|
||||
PBXFileDataSource_ObjectSize_ColumnID,
|
||||
PBXFileDataSource_Errors_ColumnID,
|
||||
PBXFileDataSource_Warnings_ColumnID,
|
||||
PBXFileDataSource_Target_ColumnID,
|
||||
);
|
||||
};
|
||||
PBXConfiguration.PBXTargetDataSource.PBXTargetDataSource = {
|
||||
PBXFileTableDataSourceColumnSortingDirectionKey = "-1";
|
||||
PBXFileTableDataSourceColumnSortingKey = PBXFileDataSource_Filename_ColumnID;
|
||||
PBXFileTableDataSourceColumnWidthsKey = (
|
||||
20,
|
||||
665,
|
||||
60,
|
||||
20,
|
||||
48,
|
||||
43,
|
||||
43,
|
||||
);
|
||||
PBXFileTableDataSourceColumnsKey = (
|
||||
PBXFileDataSource_FiletypeID,
|
||||
PBXFileDataSource_Filename_ColumnID,
|
||||
PBXTargetDataSource_PrimaryAttribute,
|
||||
PBXFileDataSource_Built_ColumnID,
|
||||
PBXFileDataSource_ObjectSize_ColumnID,
|
||||
PBXFileDataSource_Errors_ColumnID,
|
||||
PBXFileDataSource_Warnings_ColumnID,
|
||||
);
|
||||
};
|
||||
PBXPerProjectTemplateStateSaveDate = 252472219;
|
||||
PBXWorkspaceStateSaveDate = 252472219;
|
||||
};
|
||||
perUserProjectItems = {
|
||||
B60E917F0EFD7E1E000E4348 = B60E917F0EFD7E1E000E4348 /* PBXTextBookmark */;
|
||||
B60E91800EFD7E1E000E4348 = B60E91800EFD7E1E000E4348 /* PBXTextBookmark */;
|
||||
B60E91810EFD7E1E000E4348 = B60E91810EFD7E1E000E4348 /* PBXTextBookmark */;
|
||||
B60E922B0EFD94CA000E4348 = B60E922B0EFD94CA000E4348 /* PBXTextBookmark */;
|
||||
B60E929C0EFDA500000E4348 = B60E929C0EFDA500000E4348 /* PBXTextBookmark */;
|
||||
B61846170F0AF6B40045CCF7 = B61846170F0AF6B40045CCF7 /* PBXBookmark */;
|
||||
B61846570F0B02820045CCF7 = B61846570F0B02820045CCF7 /* PBXTextBookmark */;
|
||||
B61846580F0B02820045CCF7 = B61846580F0B02820045CCF7 /* PBXTextBookmark */;
|
||||
B61846590F0B02820045CCF7 = B61846590F0B02820045CCF7 /* PBXTextBookmark */;
|
||||
B634B4FA0F09BFB100D9D93E = B634B4FA0F09BFB100D9D93E /* PBXTextBookmark */;
|
||||
B6614B050EFF5F280073C4E7 = B6614B050EFF5F280073C4E7 /* PBXTextBookmark */;
|
||||
B6643BF00F0708B400630102 = B6643BF00F0708B400630102 /* PBXTextBookmark */;
|
||||
B679E4A20F02E77700FB3F0C = B679E4A20F02E77700FB3F0C /* PBXTextBookmark */;
|
||||
B679E4B60F02EFA200FB3F0C = B679E4B60F02EFA200FB3F0C /* PBXTextBookmark */;
|
||||
B679E4BB0F02EFA200FB3F0C = B679E4BB0F02EFA200FB3F0C /* PBXTextBookmark */;
|
||||
B6A271C50F040B8E00646F02 = B6A271C50F040B8E00646F02 /* PBXTextBookmark */;
|
||||
B6A271C70F040B8E00646F02 = B6A271C70F040B8E00646F02 /* PBXTextBookmark */;
|
||||
B6DBB4DE0F0C6BB400F385F2 /* PBXTextBookmark */ = B6DBB4DE0F0C6BB400F385F2 /* PBXTextBookmark */;
|
||||
B6DBB4F10F0C6CD800F385F2 /* PBXTextBookmark */ = B6DBB4F10F0C6CD800F385F2 /* PBXTextBookmark */;
|
||||
B6DBB4F20F0C6CD800F385F2 /* PBXBookmark */ = B6DBB4F20F0C6CD800F385F2 /* PBXBookmark */;
|
||||
B6DBB4F30F0C6CD800F385F2 /* PBXTextBookmark */ = B6DBB4F30F0C6CD800F385F2 /* PBXTextBookmark */;
|
||||
B6DBB4F40F0C6CD800F385F2 /* PBXTextBookmark */ = B6DBB4F40F0C6CD800F385F2 /* PBXTextBookmark */;
|
||||
B6DBB4F90F0C6DA600F385F2 /* PBXTextBookmark */ = B6DBB4F90F0C6DA600F385F2 /* PBXTextBookmark */;
|
||||
B6DBB4FC0F0C6DE600F385F2 /* PBXTextBookmark */ = B6DBB4FC0F0C6DE600F385F2 /* PBXTextBookmark */;
|
||||
};
|
||||
sourceControlManager = B60E91500EFB3628000E4348 /* Source Control */;
|
||||
userBuildSettings = {
|
||||
};
|
||||
};
|
||||
08FB7796FE84155DC02AAC07 /* xmain.cpp */ = {
|
||||
isa = PBXFileReference;
|
||||
fileEncoding = 4;
|
||||
lastKnownFileType = sourcecode.cpp.cpp;
|
||||
name = xmain.cpp;
|
||||
path = "/Users/kelvin/Projects/ProDOS-Fuse/xmain.cpp";
|
||||
sourceTree = "<absolute>";
|
||||
};
|
||||
8DD76F620486A84900D96B5E /* profuse */ = {
|
||||
activeExec = 0;
|
||||
executables = (
|
||||
B60E914A0EFB3612000E4348 /* profuse */,
|
||||
);
|
||||
};
|
||||
B60E914A0EFB3612000E4348 /* profuse */ = {
|
||||
isa = PBXExecutable;
|
||||
activeArgIndices = (
|
||||
);
|
||||
argumentStrings = (
|
||||
);
|
||||
autoAttachOnCrash = 1;
|
||||
breakpointsEnabled = 0;
|
||||
configStateDict = {
|
||||
};
|
||||
customDataFormattersEnabled = 1;
|
||||
debuggerPlugin = GDBDebugging;
|
||||
disassemblyDisplayState = 0;
|
||||
dylibVariantSuffix = "";
|
||||
enableDebugStr = 1;
|
||||
environmentEntries = (
|
||||
);
|
||||
executableSystemSymbolLevel = 0;
|
||||
executableUserSymbolLevel = 0;
|
||||
libgmallocEnabled = 0;
|
||||
name = profuse;
|
||||
savedGlobals = {
|
||||
};
|
||||
sourceDirectories = (
|
||||
);
|
||||
variableFormatDictionary = {
|
||||
"_data-char *-Disk::Read" = 1;
|
||||
"entry_length-unsigned int-Disk::ReadDirectory" = 1;
|
||||
"xcase-unsigned int-FileEntry::FileEntry" = 3;
|
||||
};
|
||||
};
|
||||
B60E914D0EFB3627000E4348 /* File.h */ = {
|
||||
uiCtxt = {
|
||||
sepNavIntBoundsRect = "{{0, 0}, {496, 2072}}";
|
||||
sepNavSelRange = "{25, 0}";
|
||||
sepNavVisRange = "{0, 1187}";
|
||||
sepNavWindowFrame = "{{95, -86}, {555, 1173}}";
|
||||
};
|
||||
};
|
||||
B60E914E0EFB3628000E4348 /* File.cpp */ = {
|
||||
uiCtxt = {
|
||||
sepNavIntBoundsRect = "{{0, 0}, {496, 3892}}";
|
||||
sepNavSelRange = "{27, 0}";
|
||||
sepNavVisRange = "{0, 1472}";
|
||||
sepNavWindowFrame = "{{667, -9}, {555, 1173}}";
|
||||
};
|
||||
};
|
||||
B60E91500EFB3628000E4348 /* Source Control */ = {
|
||||
isa = PBXSourceControlManager;
|
||||
fallbackIsa = XCSourceControlManager;
|
||||
isSCMEnabled = 0;
|
||||
scmConfiguration = {
|
||||
repositoryName = "";
|
||||
};
|
||||
};
|
||||
B60E91510EFB3628000E4348 /* Code sense */ = {
|
||||
isa = PBXCodeSenseManager;
|
||||
indexTemplatePath = "";
|
||||
};
|
||||
B60E91530EFB51FE000E4348 /* Disk.h */ = {
|
||||
uiCtxt = {
|
||||
sepNavIntBoundsRect = "{{0, 0}, {633, 1043}}";
|
||||
sepNavSelRange = "{25, 0}";
|
||||
sepNavVisRange = "{0, 1182}";
|
||||
sepNavWindowFrame = "{{77, 7}, {692, 1171}}";
|
||||
};
|
||||
};
|
||||
B60E91540EFB51FE000E4348 /* Disk.cpp */ = {
|
||||
uiCtxt = {
|
||||
sepNavIntBoundsRect = "{{0, 0}, {633, 7910}}";
|
||||
sepNavSelRange = "{27, 0}";
|
||||
sepNavVisRange = "{0, 1149}";
|
||||
sepNavWindowFrame = "{{511, 7}, {692, 1171}}";
|
||||
};
|
||||
};
|
||||
B60E91580EFD77E3000E4348 /* common.h */ = {
|
||||
uiCtxt = {
|
||||
sepNavIntBoundsRect = "{{0, 0}, {633, 1043}}";
|
||||
sepNavSelRange = "{27, 0}";
|
||||
sepNavVisRange = "{0, 474}";
|
||||
sepNavWindowFrame = "{{15, 2}, {692, 1171}}";
|
||||
};
|
||||
};
|
||||
B60E917F0EFD7E1E000E4348 /* PBXTextBookmark */ = {
|
||||
isa = PBXTextBookmark;
|
||||
fRef = B60E914D0EFB3627000E4348 /* File.h */;
|
||||
name = "File.h: 1";
|
||||
rLen = 0;
|
||||
rLoc = 0;
|
||||
rType = 0;
|
||||
vrLen = 350;
|
||||
vrLoc = 0;
|
||||
};
|
||||
B60E91800EFD7E1E000E4348 /* PBXTextBookmark */ = {
|
||||
isa = PBXTextBookmark;
|
||||
fRef = B60E91530EFB51FE000E4348 /* Disk.h */;
|
||||
name = "Disk.h: 1";
|
||||
rLen = 0;
|
||||
rLoc = 0;
|
||||
rType = 0;
|
||||
vrLen = 159;
|
||||
vrLoc = 0;
|
||||
};
|
||||
B60E91810EFD7E1E000E4348 /* PBXTextBookmark */ = {
|
||||
isa = PBXTextBookmark;
|
||||
fRef = B60E91540EFB51FE000E4348 /* Disk.cpp */;
|
||||
name = "Disk.cpp: 1";
|
||||
rLen = 0;
|
||||
rLoc = 0;
|
||||
rType = 0;
|
||||
vrLen = 292;
|
||||
vrLoc = 0;
|
||||
};
|
||||
B60E91C10EFD8049000E4348 /* xmain.cpp:129 */ = {
|
||||
isa = PBXFileBreakpoint;
|
||||
actions = (
|
||||
);
|
||||
breakpointStyle = 0;
|
||||
continueAfterActions = 0;
|
||||
countType = 0;
|
||||
delayBeforeContinue = 0;
|
||||
fileReference = 08FB7796FE84155DC02AAC07 /* xmain.cpp */;
|
||||
functionName = "main (int argc, char * const argv[])";
|
||||
hitCount = 0;
|
||||
ignoreCount = 0;
|
||||
lineNumber = 129;
|
||||
location = "ProDOS-Fuse";
|
||||
modificationTime = 251949619.113675;
|
||||
state = 2;
|
||||
};
|
||||
B60E922B0EFD94CA000E4348 /* PBXTextBookmark */ = {
|
||||
isa = PBXTextBookmark;
|
||||
fRef = B60E91580EFD77E3000E4348 /* common.h */;
|
||||
name = "common.h: 14";
|
||||
rLen = 0;
|
||||
rLoc = 146;
|
||||
rType = 0;
|
||||
vrLen = 327;
|
||||
vrLoc = 0;
|
||||
};
|
||||
B60E92720EFDA086000E4348 /* File.cpp:88 */ = {
|
||||
isa = PBXFileBreakpoint;
|
||||
actions = (
|
||||
);
|
||||
breakpointStyle = 0;
|
||||
continueAfterActions = 0;
|
||||
countType = 0;
|
||||
delayBeforeContinue = 0;
|
||||
fileReference = B60E914E0EFB3628000E4348 /* File.cpp */;
|
||||
functionName = "FileEntry::FileEntry(const void *data)";
|
||||
hitCount = 0;
|
||||
ignoreCount = 0;
|
||||
lineNumber = 88;
|
||||
location = "ProDOS-Fuse";
|
||||
modificationTime = 251949619.113693;
|
||||
state = 2;
|
||||
};
|
||||
B60E929C0EFDA500000E4348 /* PBXTextBookmark */ = {
|
||||
isa = PBXTextBookmark;
|
||||
fRef = B60E914E0EFB3628000E4348 /* File.cpp */;
|
||||
name = "File.cpp: 106";
|
||||
rLen = 0;
|
||||
rLoc = 3668;
|
||||
rType = 0;
|
||||
vrLen = 345;
|
||||
vrLoc = 1359;
|
||||
};
|
||||
B61846170F0AF6B40045CCF7 /* PBXBookmark */ = {
|
||||
isa = PBXBookmark;
|
||||
fRef = B60E91540EFB51FE000E4348 /* Disk.cpp */;
|
||||
};
|
||||
B61846570F0B02820045CCF7 /* PBXTextBookmark */ = {
|
||||
isa = PBXTextBookmark;
|
||||
fRef = B679E4A70F02E79300FB3F0C /* main.cpp */;
|
||||
name = "main.cpp: 34";
|
||||
rLen = 0;
|
||||
rLoc = 480;
|
||||
rType = 0;
|
||||
vrLen = 295;
|
||||
vrLoc = 441;
|
||||
};
|
||||
B61846580F0B02820045CCF7 /* PBXTextBookmark */ = {
|
||||
isa = PBXTextBookmark;
|
||||
fRef = B679E4A70F02E79300FB3F0C /* main.cpp */;
|
||||
name = "main.cpp: 34";
|
||||
rLen = 0;
|
||||
rLoc = 480;
|
||||
rType = 0;
|
||||
vrLen = 316;
|
||||
vrLoc = 420;
|
||||
};
|
||||
B61846590F0B02820045CCF7 /* PBXTextBookmark */ = {
|
||||
isa = PBXTextBookmark;
|
||||
fRef = B60E91540EFB51FE000E4348 /* Disk.cpp */;
|
||||
name = "Disk.cpp: 324";
|
||||
rLen = 0;
|
||||
rLoc = 7441;
|
||||
rType = 0;
|
||||
vrLen = 1213;
|
||||
vrLoc = 0;
|
||||
};
|
||||
B634B4FA0F09BFB100D9D93E /* PBXTextBookmark */ = {
|
||||
isa = PBXTextBookmark;
|
||||
fRef = B60E91540EFB51FE000E4348 /* Disk.cpp */;
|
||||
name = "Disk.cpp: 324";
|
||||
rLen = 0;
|
||||
rLoc = 7441;
|
||||
rType = 0;
|
||||
vrLen = 351;
|
||||
vrLoc = 0;
|
||||
};
|
||||
B6614B050EFF5F280073C4E7 /* PBXTextBookmark */ = {
|
||||
isa = PBXTextBookmark;
|
||||
fRef = B60E914D0EFB3627000E4348 /* File.h */;
|
||||
name = "File.h: 31";
|
||||
rLen = 0;
|
||||
rLoc = 414;
|
||||
rType = 0;
|
||||
vrLen = 330;
|
||||
vrLoc = 1231;
|
||||
};
|
||||
B6643BF00F0708B400630102 /* PBXTextBookmark */ = {
|
||||
isa = PBXTextBookmark;
|
||||
fRef = B60E914E0EFB3628000E4348 /* File.cpp */;
|
||||
name = "File.cpp: 176";
|
||||
rLen = 0;
|
||||
rLoc = 3668;
|
||||
rType = 0;
|
||||
vrLen = 505;
|
||||
vrLoc = 1290;
|
||||
};
|
||||
B679E4A20F02E77700FB3F0C /* PBXTextBookmark */ = {
|
||||
isa = PBXTextBookmark;
|
||||
fRef = B60E91580EFD77E3000E4348 /* common.h */;
|
||||
name = "common.h: 14";
|
||||
rLen = 0;
|
||||
rLoc = 146;
|
||||
rType = 0;
|
||||
vrLen = 374;
|
||||
vrLoc = 0;
|
||||
};
|
||||
B679E4A70F02E79300FB3F0C /* main.cpp */ = {
|
||||
uiCtxt = {
|
||||
sepNavIntBoundsRect = "{{0, 0}, {1353, 13692}}";
|
||||
sepNavSelRange = "{17984, 0}";
|
||||
sepNavVisRange = "{17670, 1165}";
|
||||
sepNavWindowFrame = "{{263, 144}, {1412, 924}}";
|
||||
};
|
||||
};
|
||||
B679E4B60F02EFA200FB3F0C /* PBXTextBookmark */ = {
|
||||
isa = PBXTextBookmark;
|
||||
fRef = B60E91530EFB51FE000E4348 /* Disk.h */;
|
||||
name = "Disk.h: 1";
|
||||
rLen = 0;
|
||||
rLoc = 0;
|
||||
rType = 0;
|
||||
vrLen = 342;
|
||||
vrLoc = 0;
|
||||
};
|
||||
B679E4BB0F02EFA200FB3F0C /* PBXTextBookmark */ = {
|
||||
isa = PBXTextBookmark;
|
||||
fRef = B679E4A70F02E79300FB3F0C /* main.cpp */;
|
||||
name = "main.cpp: 1";
|
||||
rLen = 0;
|
||||
rLoc = 0;
|
||||
rType = 0;
|
||||
vrLen = 356;
|
||||
vrLoc = 0;
|
||||
};
|
||||
B6A271C50F040B8E00646F02 /* PBXTextBookmark */ = {
|
||||
isa = PBXTextBookmark;
|
||||
fRef = B6AE1DAD0F03529000D36ADB /* pool.h */;
|
||||
name = "pool.h: 25";
|
||||
rLen = 0;
|
||||
rLoc = 456;
|
||||
rType = 0;
|
||||
vrLen = 571;
|
||||
vrLoc = 210;
|
||||
};
|
||||
B6A271C70F040B8E00646F02 /* PBXTextBookmark */ = {
|
||||
isa = PBXTextBookmark;
|
||||
fRef = B6AE1DAD0F03529000D36ADB /* pool.h */;
|
||||
name = "pool.h: 25";
|
||||
rLen = 0;
|
||||
rLoc = 456;
|
||||
rType = 0;
|
||||
vrLen = 571;
|
||||
vrLoc = 210;
|
||||
};
|
||||
B6AE1CFF0F0335FC00D36ADB /* main.cpp:30 */ = {
|
||||
isa = PBXFileBreakpoint;
|
||||
actions = (
|
||||
);
|
||||
breakpointStyle = 0;
|
||||
continueAfterActions = 0;
|
||||
countType = 0;
|
||||
delayBeforeContinue = 0;
|
||||
fileReference = B679E4A70F02E79300FB3F0C /* main.cpp */;
|
||||
functionName = "prodos_getattr(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)";
|
||||
hitCount = 0;
|
||||
ignoreCount = 0;
|
||||
lineNumber = 30;
|
||||
location = "ProDOS-Fuse";
|
||||
modificationTime = 251949619.113812;
|
||||
state = 2;
|
||||
};
|
||||
B6AE1DAD0F03529000D36ADB /* pool.h */ = {
|
||||
isa = PBXFileReference;
|
||||
fileEncoding = 4;
|
||||
lastKnownFileType = sourcecode.c.h;
|
||||
name = pool.h;
|
||||
path = "/Users/kelvin/Projects/ProDOS-Fuse/pool.h";
|
||||
sourceTree = "<absolute>";
|
||||
};
|
||||
B6D81E5B0EFDE859000219B7 /* xmain.cpp:163 */ = {
|
||||
isa = PBXFileBreakpoint;
|
||||
actions = (
|
||||
);
|
||||
breakpointStyle = 0;
|
||||
continueAfterActions = 0;
|
||||
countType = 0;
|
||||
delayBeforeContinue = 0;
|
||||
fileReference = 08FB7796FE84155DC02AAC07 /* xmain.cpp */;
|
||||
functionName = "main (int argc, char * const argv[])";
|
||||
hitCount = 0;
|
||||
ignoreCount = 0;
|
||||
lineNumber = 163;
|
||||
location = "ProDOS-Fuse";
|
||||
modificationTime = 251949619.113805;
|
||||
state = 2;
|
||||
};
|
||||
B6DBB4DE0F0C6BB400F385F2 /* PBXTextBookmark */ = {
|
||||
isa = PBXTextBookmark;
|
||||
fRef = B679E4A70F02E79300FB3F0C /* main.cpp */;
|
||||
name = "main.cpp: 34";
|
||||
rLen = 0;
|
||||
rLoc = 480;
|
||||
rType = 0;
|
||||
vrLen = 315;
|
||||
vrLoc = 420;
|
||||
};
|
||||
B6DBB4E70F0C6BBD00F385F2 /* profuse.1 */ = {
|
||||
uiCtxt = {
|
||||
sepNavIntBoundsRect = "{{0, 0}, {883, 1190}}";
|
||||
sepNavSelRange = "{429, 0}";
|
||||
sepNavVisRange = "{2400, 722}";
|
||||
sepNavWindowFrame = "{{15, 249}, {1412, 924}}";
|
||||
};
|
||||
};
|
||||
B6DBB4F10F0C6CD800F385F2 /* PBXTextBookmark */ = {
|
||||
isa = PBXTextBookmark;
|
||||
fRef = B679E4A70F02E79300FB3F0C /* main.cpp */;
|
||||
name = "main.cpp: 34";
|
||||
rLen = 0;
|
||||
rLoc = 480;
|
||||
rType = 0;
|
||||
vrLen = 315;
|
||||
vrLoc = 420;
|
||||
};
|
||||
B6DBB4F20F0C6CD800F385F2 /* PBXBookmark */ = {
|
||||
isa = PBXBookmark;
|
||||
fRef = B6DBB4E70F0C6BBD00F385F2 /* profuse.1 */;
|
||||
};
|
||||
B6DBB4F30F0C6CD800F385F2 /* PBXTextBookmark */ = {
|
||||
isa = PBXTextBookmark;
|
||||
fRef = B679E4A70F02E79300FB3F0C /* main.cpp */;
|
||||
name = "main.cpp: 34";
|
||||
rLen = 0;
|
||||
rLoc = 480;
|
||||
rType = 0;
|
||||
vrLen = 315;
|
||||
vrLoc = 420;
|
||||
};
|
||||
B6DBB4F40F0C6CD800F385F2 /* PBXTextBookmark */ = {
|
||||
isa = PBXTextBookmark;
|
||||
fRef = B6DBB4E70F0C6BBD00F385F2 /* profuse.1 */;
|
||||
name = "profuse.1: 10";
|
||||
rLen = 0;
|
||||
rLoc = 429;
|
||||
rType = 0;
|
||||
vrLen = 722;
|
||||
vrLoc = 2400;
|
||||
};
|
||||
B6DBB4F90F0C6DA600F385F2 /* PBXTextBookmark */ = {
|
||||
isa = PBXTextBookmark;
|
||||
fRef = B6DBB4E70F0C6BBD00F385F2 /* profuse.1 */;
|
||||
name = "profuse.1: 10";
|
||||
rLen = 0;
|
||||
rLoc = 429;
|
||||
rType = 0;
|
||||
vrLen = 722;
|
||||
vrLoc = 2400;
|
||||
};
|
||||
B6DBB4FC0F0C6DE600F385F2 /* PBXTextBookmark */ = {
|
||||
isa = PBXTextBookmark;
|
||||
fRef = B6DBB4E70F0C6BBD00F385F2 /* profuse.1 */;
|
||||
name = "profuse.1: 10";
|
||||
rLen = 0;
|
||||
rLoc = 429;
|
||||
rType = 0;
|
||||
vrLen = 722;
|
||||
vrLoc = 2400;
|
||||
};
|
||||
}
|
283
profuse.xcodeproj/project.pbxproj
Normal file
283
profuse.xcodeproj/project.pbxproj
Normal file
@ -0,0 +1,283 @@
|
||||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 45;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
B60E914F0EFB3628000E4348 /* File.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B60E914E0EFB3628000E4348 /* File.cpp */; };
|
||||
B60E91550EFB51FE000E4348 /* Disk.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B60E91540EFB51FE000E4348 /* Disk.cpp */; };
|
||||
B679E49B0F02E71400FB3F0C /* libfuse_ino64.2.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = B679E49A0F02E71400FB3F0C /* libfuse_ino64.2.dylib */; };
|
||||
B679E4A80F02E79300FB3F0C /* main.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B679E4A70F02E79300FB3F0C /* main.cpp */; };
|
||||
B6AE1D530F033B5E00D36ADB /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B6AE1D520F033B5E00D36ADB /* CoreFoundation.framework */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
8DD76F690486A84900D96B5E /* CopyFiles */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 8;
|
||||
dstPath = /usr/share/man/man1/;
|
||||
dstSubfolderSpec = 0;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 1;
|
||||
};
|
||||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
8DD76F6C0486A84900D96B5E /* profuse */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = profuse; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
B60E914D0EFB3627000E4348 /* File.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = File.h; sourceTree = "<group>"; };
|
||||
B60E914E0EFB3628000E4348 /* File.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = File.cpp; sourceTree = "<group>"; };
|
||||
B60E91530EFB51FE000E4348 /* Disk.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Disk.h; sourceTree = "<group>"; };
|
||||
B60E91540EFB51FE000E4348 /* Disk.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Disk.cpp; sourceTree = "<group>"; };
|
||||
B60E91580EFD77E3000E4348 /* common.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = common.h; sourceTree = "<group>"; };
|
||||
B679E49A0F02E71400FB3F0C /* libfuse_ino64.2.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libfuse_ino64.2.dylib; path = /usr/local/lib/libfuse_ino64.2.dylib; sourceTree = "<absolute>"; };
|
||||
B679E4A70F02E79300FB3F0C /* main.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = main.cpp; sourceTree = "<group>"; };
|
||||
B6AE1D520F033B5E00D36ADB /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = /System/Library/Frameworks/CoreFoundation.framework; sourceTree = "<absolute>"; };
|
||||
B6DBB4E70F0C6BBD00F385F2 /* profuse.1 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.man; path = profuse.1; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
8DD76F660486A84900D96B5E /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
B679E49B0F02E71400FB3F0C /* libfuse_ino64.2.dylib in Frameworks */,
|
||||
B6AE1D530F033B5E00D36ADB /* CoreFoundation.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
08FB7794FE84155DC02AAC07 /* ProDOS-Fuse */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
08FB7795FE84155DC02AAC07 /* Source */,
|
||||
C6859E8C029090F304C91782 /* Documentation */,
|
||||
1AB674ADFE9D54B511CA2CBB /* Products */,
|
||||
B679E49D0F02E71D00FB3F0C /* Libraries */,
|
||||
);
|
||||
name = "ProDOS-Fuse";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
08FB7795FE84155DC02AAC07 /* Source */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B60E91580EFD77E3000E4348 /* common.h */,
|
||||
B60E91530EFB51FE000E4348 /* Disk.h */,
|
||||
B60E91540EFB51FE000E4348 /* Disk.cpp */,
|
||||
B60E914D0EFB3627000E4348 /* File.h */,
|
||||
B60E914E0EFB3628000E4348 /* File.cpp */,
|
||||
B679E4A70F02E79300FB3F0C /* main.cpp */,
|
||||
);
|
||||
name = Source;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
1AB674ADFE9D54B511CA2CBB /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
8DD76F6C0486A84900D96B5E /* profuse */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B679E49D0F02E71D00FB3F0C /* Libraries */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B6AE1D520F033B5E00D36ADB /* CoreFoundation.framework */,
|
||||
B679E49A0F02E71400FB3F0C /* libfuse_ino64.2.dylib */,
|
||||
);
|
||||
name = Libraries;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
C6859E8C029090F304C91782 /* Documentation */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B6DBB4E70F0C6BBD00F385F2 /* profuse.1 */,
|
||||
);
|
||||
name = Documentation;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
8DD76F620486A84900D96B5E /* profuse */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 1DEB923108733DC60010E9CD /* Build configuration list for PBXNativeTarget "profuse" */;
|
||||
buildPhases = (
|
||||
8DD76F640486A84900D96B5E /* Sources */,
|
||||
8DD76F660486A84900D96B5E /* Frameworks */,
|
||||
8DD76F690486A84900D96B5E /* CopyFiles */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = profuse;
|
||||
productInstallPath = "$(HOME)/bin";
|
||||
productName = "ProDOS-Fuse";
|
||||
productReference = 8DD76F6C0486A84900D96B5E /* profuse */;
|
||||
productType = "com.apple.product-type.tool";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
08FB7793FE84155DC02AAC07 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
buildConfigurationList = 1DEB923508733DC60010E9CD /* Build configuration list for PBXProject "profuse" */;
|
||||
compatibilityVersion = "Xcode 3.1";
|
||||
hasScannedForEncodings = 1;
|
||||
mainGroup = 08FB7794FE84155DC02AAC07 /* ProDOS-Fuse */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
8DD76F620486A84900D96B5E /* profuse */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
8DD76F640486A84900D96B5E /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
B60E914F0EFB3628000E4348 /* File.cpp in Sources */,
|
||||
B60E91550EFB51FE000E4348 /* Disk.cpp in Sources */,
|
||||
B679E4A80F02E79300FB3F0C /* main.cpp in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
1DEB923208733DC60010E9CD /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_ENABLE_FIX_AND_CONTINUE = YES;
|
||||
GCC_MODEL_TUNING = G5;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"_GLIBCXX_DEBUG=1",
|
||||
"_GLIBCXX_DEBUG_PEDANTIC=1",
|
||||
);
|
||||
INSTALL_PATH = /usr/local/bin;
|
||||
PRODUCT_NAME = "ProDOS-Fuse";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
1DEB923308733DC60010E9CD /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
GCC_MODEL_TUNING = G5;
|
||||
INSTALL_PATH = /usr/local/bin;
|
||||
PRODUCT_NAME = "ProDOS-Fuse";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
1DEB923608733DC60010E9CD /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ARCHS = "$(ARCHS_STANDARD_32_BIT)";
|
||||
GCC_C_LANGUAGE_STANDARD = c99;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = "";
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
HEADER_SEARCH_PATHS = /usr/local/include/fuse;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
OTHER_CFLAGS = (
|
||||
"-D__FreeBSD__=10",
|
||||
"-D_FILE_OFFSET_BITS=64",
|
||||
"-D__DARWIN_64_BIT_INO_T=1",
|
||||
);
|
||||
PREBINDING = NO;
|
||||
SDKROOT = macosx10.5;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
1DEB923708733DC60010E9CD /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ARCHS = "$(ARCHS_STANDARD_32_BIT)";
|
||||
GCC_C_LANGUAGE_STANDARD = c99;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
PREBINDING = NO;
|
||||
SDKROOT = macosx10.5;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
B618461A0F0AF6CC0045CCF7 /* Debug Universal */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ARCHS = "$(ARCHS_STANDARD_32_64_BIT)";
|
||||
GCC_C_LANGUAGE_STANDARD = c99;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = "";
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
HEADER_SEARCH_PATHS = /usr/local/include/fuse;
|
||||
ONLY_ACTIVE_ARCH = NO;
|
||||
OTHER_CFLAGS = (
|
||||
"-D__FreeBSD__=10",
|
||||
"-D_FILE_OFFSET_BITS=64",
|
||||
"-D__DARWIN_64_BIT_INO_T=1",
|
||||
);
|
||||
PREBINDING = NO;
|
||||
SDKROOT = macosx10.5;
|
||||
};
|
||||
name = "Debug Universal";
|
||||
};
|
||||
B618461B0F0AF6CC0045CCF7 /* Debug Universal */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_ENABLE_FIX_AND_CONTINUE = YES;
|
||||
GCC_MODEL_TUNING = G5;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"_GLIBCXX_DEBUG=1",
|
||||
"_GLIBCXX_DEBUG_PEDANTIC=1",
|
||||
);
|
||||
INSTALL_PATH = /usr/local/bin;
|
||||
PRODUCT_NAME = profuse;
|
||||
};
|
||||
name = "Debug Universal";
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
1DEB923108733DC60010E9CD /* Build configuration list for PBXNativeTarget "profuse" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
1DEB923208733DC60010E9CD /* Debug */,
|
||||
B618461B0F0AF6CC0045CCF7 /* Debug Universal */,
|
||||
1DEB923308733DC60010E9CD /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
1DEB923508733DC60010E9CD /* Build configuration list for PBXProject "profuse" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
1DEB923608733DC60010E9CD /* Debug */,
|
||||
B618461A0F0AF6CC0045CCF7 /* Debug Universal */,
|
||||
1DEB923708733DC60010E9CD /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = 08FB7793FE84155DC02AAC07 /* Project object */;
|
||||
}
|
Loading…
Reference in New Issue
Block a user