mirror of
https://github.com/ksherlock/profuse.git
synced 2025-01-10 07:30:09 +00:00
89e80dcc10
git-svn-id: https://profuse.googlecode.com/svn/branches/v2@350 aa027e90-d47c-11dd-86d7-074df07e0730
1072 lines
24 KiB
C++
1072 lines
24 KiB
C++
/*
|
|
* FileMan utilities.
|
|
*
|
|
* L - list dir
|
|
* E -
|
|
*/
|
|
|
|
#include <cstdio>
|
|
#include <cstring>
|
|
#include <cstdlib>
|
|
#include <cstdarg>
|
|
#include <cerrno>
|
|
|
|
#include <algorithm>
|
|
#include <memory>
|
|
|
|
#include <unistd.h>
|
|
#include <strings.h>
|
|
#include <sys/stat.h>
|
|
|
|
#include <Pascal/Pascal.h>
|
|
#include <Pascal/Date.h>
|
|
#include <Pascal/TextWriter.h>
|
|
|
|
#include <Device/Device.h>
|
|
#include <Device/BlockDevice.h>
|
|
|
|
#include <File/File.h>
|
|
#include <File/MappedFile.h>
|
|
|
|
|
|
enum commands {
|
|
kCommandLS = 1,
|
|
kCommandCAT,
|
|
kCommandCP,
|
|
kCommandMV,
|
|
kCommandRM,
|
|
kCommandGET,
|
|
kCommandPUT,
|
|
kCommandKRUNCH
|
|
};
|
|
|
|
|
|
void usage()
|
|
{
|
|
std::fputs(
|
|
"Pascal File Manager v 0.0\n\n"
|
|
"Usage: apfm [-h] [-f format] diskimage action ...\n"
|
|
"Options:\n"
|
|
" -h Show usage information.\n"
|
|
" -f format Specify disk format. Valid values are:\n"
|
|
" po: ProDOS order disk image\n"
|
|
" do: DOS Order disk image\n"
|
|
"\n"
|
|
"Actions:\n"
|
|
" cat\n"
|
|
" krunch\n"
|
|
" ls\n"
|
|
" cp\n"
|
|
" mv\n"
|
|
" rm\n"
|
|
" get\n"
|
|
" put\n",
|
|
stdout
|
|
);
|
|
|
|
}
|
|
|
|
void commandUsage(unsigned command)
|
|
{
|
|
const char *text = "";
|
|
|
|
switch(command)
|
|
{
|
|
case kCommandLS:
|
|
text =
|
|
"List the volume contents.\n\n"
|
|
"apfm file ls [-l]\n"
|
|
"Options:\n"
|
|
" -l Extended listing\n"
|
|
;
|
|
break;
|
|
|
|
|
|
case kCommandKRUNCH:
|
|
text =
|
|
"Move free blocks to the end of the volume.\n\n"
|
|
"apfm krunch [-fi]\n"
|
|
"Options:\n"
|
|
" -f Force\n"
|
|
" -i Interactive\n"
|
|
;
|
|
break;
|
|
|
|
case kCommandCAT:
|
|
text =
|
|
"Print the contents of a file to stdout.\n\n"
|
|
"apfm cat file ...\n"
|
|
;
|
|
break;
|
|
|
|
case kCommandCP:
|
|
text =
|
|
"Copy a file.\n\n"
|
|
"apfm cp [-fi] source target\n"
|
|
"Options:\n"
|
|
" -f Force\n"
|
|
" -i Interactive\n"
|
|
;
|
|
break;
|
|
|
|
case kCommandMV:
|
|
text =
|
|
"Rename a file.\n\n"
|
|
"apfm mv [-fi] source target\n"
|
|
"Options:\n"
|
|
" -f Force\n"
|
|
" -i Interactive\n"
|
|
;
|
|
break;
|
|
|
|
case kCommandGET:
|
|
text =
|
|
"Copy a file from the Pascal volume to the native file system.\n\n"
|
|
"apfm get [-fi] source [target]\n"
|
|
"Options:\n"
|
|
" -f Force\n"
|
|
" -i Interactive\n"
|
|
;
|
|
break;
|
|
|
|
case kCommandPUT:
|
|
text =
|
|
"Copy a file from the native file system to the Pascal volume.\n\n"
|
|
"apfm put [-fi] source [target]\n"
|
|
"Options:\n"
|
|
" -f Force\n"
|
|
" -i Interactive\n"
|
|
" -t type Set the file type. Valid values:\n"
|
|
" code\n"
|
|
" data\n"
|
|
" foto\n"
|
|
" graf\n"
|
|
" info\n"
|
|
" text\n"
|
|
;
|
|
break;
|
|
}
|
|
|
|
std::fputs(text, stdout);
|
|
}
|
|
|
|
|
|
unsigned command(const char *command)
|
|
{
|
|
if (!::strcasecmp(command, "ls")) return kCommandLS;
|
|
if (!::strcasecmp(command, "list")) return kCommandLS;
|
|
|
|
if (!::strcasecmp(command, "cat")) return kCommandCAT;
|
|
|
|
if (!::strcasecmp(command, "get")) return kCommandGET;
|
|
|
|
if (!::strcasecmp(command, "put")) return kCommandPUT;
|
|
|
|
if (!::strcasecmp(command, "cp")) return kCommandCP;
|
|
if (!::strcasecmp(command, "copy")) return kCommandCP;
|
|
|
|
if (!::strcasecmp(command, "rm")) return kCommandRM;
|
|
if (!::strcasecmp(command, "remove")) return kCommandRM;
|
|
if (!::strcasecmp(command, "del")) return kCommandRM;
|
|
if (!::strcasecmp(command, "delete")) return kCommandRM;
|
|
|
|
if (!::strcasecmp(command, "mv")) return kCommandMV;
|
|
if (!::strcasecmp(command, "move")) return kCommandMV;
|
|
if (!::strcasecmp(command, "rename")) return kCommandMV;
|
|
|
|
if (!::strcasecmp(command, "krunch")) return kCommandKRUNCH;
|
|
|
|
|
|
return -1;
|
|
}
|
|
|
|
File::FileFlags commandFlags(unsigned command)
|
|
{
|
|
switch (command)
|
|
{
|
|
case kCommandPUT:
|
|
case kCommandKRUNCH:
|
|
case kCommandRM:
|
|
case kCommandMV:
|
|
case kCommandCP:
|
|
return File::ReadWrite;
|
|
default:
|
|
return File::ReadOnly;
|
|
}
|
|
}
|
|
|
|
// from BSD rm, prompt question on stderr.
|
|
|
|
bool yes_or_no(const char *format, ...)
|
|
{
|
|
int ch, first;
|
|
va_list va;
|
|
|
|
va_start(va, format);
|
|
std::vfprintf(stderr, format, va);
|
|
va_end(va);
|
|
|
|
std::fputc(' ', stderr);
|
|
std::fflush(stderr);
|
|
|
|
first = ch = getchar();
|
|
while (ch != '\n' && ch != EOF)
|
|
ch = getchar();
|
|
return (first == 'y' || first == 'Y');
|
|
}
|
|
|
|
|
|
const char *MonthName(unsigned m)
|
|
{
|
|
static const char *months[] = {
|
|
"",
|
|
"Jan",
|
|
"Feb",
|
|
"Mar",
|
|
"Apr",
|
|
"May",
|
|
"Jun",
|
|
"Jul",
|
|
"Aug",
|
|
"Sep",
|
|
"Oct",
|
|
"Nov",
|
|
"Dec"
|
|
};
|
|
|
|
if (m > 12) return "";
|
|
return months[m];
|
|
}
|
|
|
|
const char *FileType(unsigned ft)
|
|
{
|
|
static const char *types[] = {
|
|
"Unknown",
|
|
"Badblocks",
|
|
"Codefile",
|
|
"Textfile",
|
|
"Infofile",
|
|
"Datafile",
|
|
"Graffile",
|
|
"Fotofile",
|
|
"SecureDir"
|
|
|
|
};
|
|
|
|
if (ft < 8) return types[ft];
|
|
|
|
return "";
|
|
}
|
|
|
|
void printUnusedEntry(unsigned block, unsigned size)
|
|
{
|
|
std::printf("< UNUSED > %4u %4u\n", size, block);
|
|
}
|
|
|
|
void printFileEntry(Pascal::FileEntryPointer e, bool extended)
|
|
{
|
|
Pascal::Date dt = e->modification();
|
|
|
|
if (extended)
|
|
{
|
|
std::printf("%-15s %4u %2u-%s-%2u %5u %5u %s\n",
|
|
e->name(),
|
|
e->blocks(),
|
|
dt.day(),
|
|
MonthName(dt.month()),
|
|
dt.year() % 100,
|
|
e->firstBlock(),
|
|
e->lastByte(),
|
|
FileType(e->fileKind())
|
|
);
|
|
|
|
}
|
|
else
|
|
{
|
|
std::printf("%-15s %4u %2u-%s-%2u\n",
|
|
e->name(),
|
|
e->blocks(),
|
|
dt.day(),
|
|
MonthName(dt.month()),
|
|
dt.year() % 100
|
|
);
|
|
}
|
|
|
|
}
|
|
|
|
int action_ls(int argc, char **argv, Pascal::VolumeEntry *volume)
|
|
{
|
|
bool extended = false;
|
|
unsigned fileCount = volume->fileCount();
|
|
unsigned used = volume->blocks();
|
|
unsigned max = 0;
|
|
unsigned volumeSize = volume->volumeBlocks();
|
|
unsigned lastBlock = volume->lastBlock();
|
|
int c;
|
|
|
|
std::fprintf(stdout, "%s:\n", volume->name());
|
|
|
|
//argv[0] = "afpm ls";
|
|
|
|
while ((c = ::getopt(argc, argv, "lh")) != -1)
|
|
{
|
|
switch(c)
|
|
{
|
|
case 'l':
|
|
extended = true;
|
|
break;
|
|
case 'h':
|
|
default:
|
|
commandUsage(kCommandLS);
|
|
return c == 'h' ? 0 : 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
argc -= optind;
|
|
argv += optind;
|
|
// only print listed files?
|
|
|
|
for (unsigned i = 0; i < fileCount; ++i)
|
|
{
|
|
Pascal::FileEntryPointer e = volume->fileAtIndex(i);
|
|
if (!e) continue;
|
|
|
|
|
|
if (lastBlock != e->firstBlock())
|
|
{
|
|
unsigned size = e->firstBlock() - lastBlock;
|
|
max = std::max(max, size);
|
|
|
|
if (extended)
|
|
{
|
|
printUnusedEntry(lastBlock, size);
|
|
}
|
|
}
|
|
|
|
printFileEntry(e, extended);
|
|
|
|
lastBlock = e->lastBlock();
|
|
used += e->blocks();
|
|
}
|
|
|
|
if (lastBlock != volumeSize)
|
|
{
|
|
unsigned size = volumeSize - lastBlock;
|
|
max = std::max(max, size);
|
|
if (extended)
|
|
printUnusedEntry(lastBlock, size);
|
|
}
|
|
|
|
|
|
std::fprintf(stdout,
|
|
"%u/%u files <listed/in-dir>, "
|
|
"%u blocks used, "
|
|
"%u unused, "
|
|
"%u in largest\n",
|
|
fileCount, fileCount,
|
|
used,
|
|
volumeSize - used,
|
|
max
|
|
);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int action_cat(unsigned argc, char **argv, Pascal::VolumeEntry *volume)
|
|
{
|
|
// cat file1, file2...
|
|
//argv[0] = "afpm cat";
|
|
|
|
int c;
|
|
|
|
while ((c = ::getopt(argc, argv, "h")) != -1)
|
|
{
|
|
switch(c)
|
|
{
|
|
case 'h':
|
|
default:
|
|
commandUsage(kCommandCAT);
|
|
return c == 'h' ? 0 : 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
argc -= optind;
|
|
argv += optind;
|
|
|
|
if (argc < 1)
|
|
{
|
|
commandUsage(kCommandCAT);
|
|
return -1;
|
|
}
|
|
|
|
for (unsigned i = 0; i < argc; ++i)
|
|
{
|
|
const char *fname = argv[i];
|
|
unsigned fileSize;
|
|
unsigned offset;
|
|
uint8_t buffer[512];
|
|
Pascal::FileEntryPointer e;
|
|
// find it...
|
|
|
|
e = volume->fileByName(argv[i]);
|
|
|
|
if (!e)
|
|
{
|
|
std::fprintf(stderr, "apfm cat: %s: no such file.\n", fname);
|
|
continue;
|
|
}
|
|
|
|
fileSize = e->fileSize();
|
|
offset = 0;
|
|
while (offset < fileSize)
|
|
{
|
|
unsigned count = std::min(512u, fileSize - offset);
|
|
e->read(buffer, count, offset);
|
|
|
|
std::fwrite(buffer, count, 1, stdout);
|
|
offset += count;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
int action_mv(int argc, char **argv, Pascal::VolumeEntry *volume)
|
|
{
|
|
// mv src dest
|
|
bool iFlag = ::isatty(STDIN_FILENO);
|
|
int c;
|
|
|
|
while ((c = ::getopt(argc, argv, "fih")) != -1)
|
|
{
|
|
switch(c)
|
|
{
|
|
case 'f':
|
|
iFlag = false;
|
|
break;
|
|
case 'i':
|
|
iFlag = true;
|
|
break;
|
|
|
|
case 'h':
|
|
default:
|
|
commandUsage(kCommandMV);
|
|
return c == 'h' ? 0 : 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
argc -= optind;
|
|
argv += optind;
|
|
|
|
if (argc != 2)
|
|
{
|
|
commandUsage(kCommandMV);
|
|
return -1;
|
|
}
|
|
|
|
const char *source = argv[0];
|
|
const char *dest = argv[1];
|
|
|
|
if (!volume->fileByName(source))
|
|
{
|
|
std::fprintf(stderr, "apfm mv: %s: no such file.\n", source);
|
|
return -1;
|
|
}
|
|
|
|
// if -i and destination file exists, confirm overwritting it.
|
|
if (iFlag && volume->fileByName(dest))
|
|
{
|
|
bool ok = yes_or_no("Overwrite %s?", dest);
|
|
if (!ok)
|
|
{
|
|
std::fprintf(stderr, "Not overwritten.\n");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
volume->rename(source, dest);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int action_cp(int argc, char **argv, Pascal::VolumeEntry *volume)
|
|
{
|
|
// cp src dest
|
|
|
|
bool iFlag = ::isatty(STDIN_FILENO);
|
|
int c;
|
|
|
|
while ((c = ::getopt(argc, argv, "fih")) != -1)
|
|
{
|
|
switch(c)
|
|
{
|
|
case 'f':
|
|
iFlag = false;
|
|
break;
|
|
case 'i':
|
|
iFlag = true;
|
|
break;
|
|
|
|
case 'h':
|
|
default:
|
|
commandUsage(kCommandCP);
|
|
return c == 'h' ? 0 : 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
argc -= optind;
|
|
argv += optind;
|
|
|
|
if (argc != 2)
|
|
{
|
|
commandUsage(kCommandCP);
|
|
return -1;
|
|
}
|
|
|
|
const char *source = argv[0];
|
|
const char *dest = argv[1];
|
|
|
|
if (!volume->fileByName(source))
|
|
{
|
|
std::fprintf(stderr, "apfm cp: %s: no such file.\n", source);
|
|
return -1;
|
|
}
|
|
|
|
// if -i and destination file exists, confirm overwritting it.
|
|
if (iFlag && volume->fileByName(dest))
|
|
{
|
|
bool ok = yes_or_no("Overwrite %s?", dest);
|
|
if (!ok)
|
|
{
|
|
std::fprintf(stderr, "Not overwritten.\n");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
volume->copy(source, dest);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int action_rm(int argc, char **argv, Pascal::VolumeEntry *volume)
|
|
{
|
|
// rm file [file ....]
|
|
|
|
bool iFlag = ::isatty(STDIN_FILENO);
|
|
int c;
|
|
|
|
while ((c = ::getopt(argc, argv, "fih")) != -1)
|
|
{
|
|
switch(c)
|
|
{
|
|
case 'f':
|
|
iFlag = false;
|
|
break;
|
|
case 'i':
|
|
iFlag = true;
|
|
break;
|
|
|
|
case 'h':
|
|
default:
|
|
commandUsage(kCommandRM);
|
|
return c == 'h' ? 0 : 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
argc -= optind;
|
|
argv += optind;
|
|
|
|
// TODO -- catch errors ?
|
|
for (int i = 0; i < argc; ++i)
|
|
{
|
|
Pascal::FileEntryPointer e = volume->fileByName(argv[i]);
|
|
|
|
if (!e)
|
|
{
|
|
if (iFlag) std::fprintf(stderr, "apfm rm: %s: No such file.\n", argv[i]);
|
|
continue;
|
|
}
|
|
|
|
if (iFlag)
|
|
{
|
|
bool ok = yes_or_no("Remove %s?", argv[i]);
|
|
if (!ok) continue;
|
|
}
|
|
|
|
volume->unlink(argv[i]);
|
|
}
|
|
return 0;
|
|
|
|
}
|
|
|
|
int action_krunch(int argc, char **argv, Pascal::VolumeEntry *volume)
|
|
{
|
|
// compress file to remove gaps.
|
|
|
|
bool iFlag = ::isatty(STDIN_FILENO);
|
|
int c;
|
|
|
|
while ((c = ::getopt(argc, argv, "fih")) != -1)
|
|
{
|
|
switch(c)
|
|
{
|
|
case 'f':
|
|
iFlag = false;
|
|
break;
|
|
case 'i':
|
|
iFlag = true;
|
|
break;
|
|
|
|
case 'h':
|
|
default:
|
|
commandUsage(kCommandKRUNCH);
|
|
return c == 'h' ? 0 : 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
argc -= optind;
|
|
argv += optind;
|
|
|
|
|
|
|
|
if (!volume->canKrunch())
|
|
{
|
|
if (iFlag)
|
|
std::fprintf(stderr, "apfm krunch: Volume already optimized.\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
if (iFlag)
|
|
{
|
|
bool ok = yes_or_no("Are you sure you want to krunch this volume?");
|
|
if (!ok) return -1;
|
|
}
|
|
|
|
volume->krunch();
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
int action_get(int argc, char **argv, Pascal::VolumeEntry *volume)
|
|
{
|
|
// get [-f] pascal_file [native file];
|
|
|
|
char *infile;
|
|
char *outfile;
|
|
|
|
bool iFlag = ::isatty(STDIN_FILENO);
|
|
int c;
|
|
|
|
while ((c = ::getopt(argc, argv, "fih")) != -1)
|
|
{
|
|
switch(c)
|
|
{
|
|
case 'f':
|
|
iFlag = false;
|
|
break;
|
|
case 'i':
|
|
iFlag = true;
|
|
break;
|
|
|
|
case 'h':
|
|
default:
|
|
commandUsage(kCommandGET);
|
|
return c == 'h' ? 0 : -1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
argc -= optind;
|
|
argv += optind;
|
|
|
|
Pascal::FileEntryPointer entry;
|
|
|
|
switch(argc)
|
|
{
|
|
case 1:
|
|
infile = outfile = argv[0];
|
|
break;
|
|
case 2:
|
|
infile = argv[0];
|
|
outfile = argv[1];
|
|
break;
|
|
default:
|
|
commandUsage(kCommandGET);
|
|
return -1;
|
|
break;
|
|
}
|
|
|
|
entry = volume->fileByName(infile);
|
|
|
|
|
|
if (!entry)
|
|
{
|
|
std::fprintf(stderr, "apfm get: %s: no such file.\n", infile);
|
|
return -1;
|
|
}
|
|
|
|
|
|
// if not -f, check before overwriting file.
|
|
if (iFlag)
|
|
{
|
|
struct stat st;
|
|
if (::stat(outfile, &st) == 0)
|
|
{
|
|
bool ok = yes_or_no("Overwrite %s?", outfile);
|
|
if (!ok)
|
|
{
|
|
std::fprintf(stderr, "Not overwritten.\n");
|
|
return -1;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
File::File file(outfile, O_WRONLY | O_CREAT | O_TRUNC, 0666);
|
|
|
|
unsigned fileSize = entry->fileSize();
|
|
unsigned offset = 0;
|
|
|
|
while (offset < fileSize)
|
|
{
|
|
uint8_t buffer[512];
|
|
|
|
unsigned count = std::min(512u, fileSize - offset);
|
|
entry->read(buffer, count, offset);
|
|
|
|
::write(file.fd(), buffer, count);
|
|
offset += count;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int action_put(int argc, char **argv, Pascal::VolumeEntry *volume)
|
|
{
|
|
// put [-t type] native_file [pascal_file]
|
|
|
|
unsigned type = Pascal::kUntypedFile;
|
|
|
|
struct stat st;
|
|
int c;
|
|
|
|
char *infile;
|
|
char *outfile;
|
|
char *tmp;
|
|
bool iFlag = ::isatty(STDIN_FILENO);
|
|
bool tFlag = false;
|
|
|
|
while ((c = getopt(argc, argv, "fhit:")) != -1)
|
|
{
|
|
switch (c)
|
|
{
|
|
case 't':
|
|
tFlag = true;
|
|
|
|
if (!::strcasecmp("text", optarg))
|
|
type = Pascal::kTextFile;
|
|
else if (!::strcasecmp("txt", optarg))
|
|
type = Pascal::kTextFile;
|
|
else if (!::strcasecmp("code", optarg))
|
|
type = Pascal::kCodeFile;
|
|
else if (!::strcasecmp("info", optarg))
|
|
type = Pascal::kInfoFile;
|
|
else if (!::strcasecmp("data", optarg))
|
|
type = Pascal::kDataFile;
|
|
else if (!::strcasecmp("graf", optarg))
|
|
type = Pascal::kGrafFile;
|
|
else if (!::strcasecmp("foto", optarg))
|
|
type = Pascal::kFotoFile;
|
|
else type = Pascal::kUntypedFile;
|
|
|
|
break;
|
|
|
|
case 'i':
|
|
iFlag = true;
|
|
break;
|
|
case 'f':
|
|
iFlag = false;
|
|
break;
|
|
case 'h':
|
|
default:
|
|
commandUsage(kCommandPUT);
|
|
return c == 'h' ? 0 : -1;
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
argc -= optind;
|
|
argv += optind;
|
|
|
|
switch (argc)
|
|
{
|
|
case 1:
|
|
infile = outfile = argv[0];
|
|
|
|
// need to run basename on outfile.
|
|
tmp = strrchr(outfile, '/');
|
|
if (tmp) outfile = tmp + 1;
|
|
break;
|
|
case 2:
|
|
infile = argv[0];
|
|
outfile = argv[1];
|
|
break;
|
|
default:
|
|
commandUsage(kCommandPUT);
|
|
return -1;
|
|
break;
|
|
}
|
|
|
|
|
|
// if no tFlag specified, check .text/.txt
|
|
if (!tFlag)
|
|
{
|
|
tmp = strrchr(outfile, '.');
|
|
if (tmp)
|
|
{
|
|
if (::strcasecmp(tmp, ".text") == 0 || strcasecmp(tmp, ".txt") == 0)
|
|
type = Pascal::kTextFile;
|
|
}
|
|
}
|
|
|
|
if (!Pascal::FileEntry::ValidName(outfile))
|
|
{
|
|
std::fprintf(stderr, "apfm put: `%s' is not a valid pascal name.\n", outfile);
|
|
}
|
|
|
|
if (::stat(infile, &st) != 0)
|
|
{
|
|
std::fprintf(stderr, "apfm put: %s: no such file.\n", infile);
|
|
return -1;
|
|
}
|
|
|
|
if (!S_ISREG(st.st_mode))
|
|
{
|
|
std::fprintf(stderr, "apfm put: %s: not a regular file.\n", infile);
|
|
return -1;
|
|
}
|
|
|
|
unsigned blocks = (st.st_size + 511) / 512;
|
|
if (blocks > (0xffff - 6))
|
|
{
|
|
std::fprintf(stderr, "apfm put: %s: file is too large.\n", infile);
|
|
return -1;
|
|
}
|
|
|
|
|
|
|
|
File file(infile, File::ReadOnly);
|
|
MappedFile mf(file, File::ReadOnly, st.st_size);
|
|
|
|
|
|
|
|
|
|
|
|
if (type == Pascal::kTextFile)
|
|
{
|
|
Pascal::TextWriter text;
|
|
|
|
|
|
const char *address = (const char *)mf.address();
|
|
|
|
unsigned start = 0;
|
|
unsigned length = st.st_size;
|
|
|
|
for (unsigned i = 0; i < length; ++i)
|
|
{
|
|
char c = address[i];
|
|
if (c == 0x0d || c == 0x0a)
|
|
{
|
|
//std::fprintf(stdout, "%.*s\n", i - start, address + start);
|
|
text.writeLine(address + start, i - start);
|
|
start = i + 1;
|
|
|
|
if (c == 0x0a && i < length && address[i +1 ] == 0x0d)
|
|
{
|
|
++start;
|
|
++i;
|
|
}
|
|
}
|
|
}
|
|
|
|
//any remainder.
|
|
if (start != length) text.writeLine(address + start, length - start);
|
|
|
|
blocks = text.blocks();
|
|
|
|
Pascal::FileEntryPointer entry = volume->create(outfile, blocks);
|
|
if (!entry)
|
|
{
|
|
perror(NULL);
|
|
return -1;
|
|
}
|
|
|
|
entry->setFileKind(type);
|
|
entry->write(text);
|
|
|
|
}
|
|
else
|
|
{
|
|
Pascal::FileEntryPointer entry = volume->create(outfile, blocks);
|
|
if (!entry)
|
|
{
|
|
perror(NULL);
|
|
return -1;
|
|
}
|
|
|
|
entry->setFileKind(type);
|
|
|
|
unsigned remaining = st.st_size;
|
|
unsigned offset = 0;
|
|
const uint8_t *address = (const uint8_t *)mf.address();
|
|
while (remaining)
|
|
{
|
|
int rv;
|
|
unsigned count = std::min(512u, remaining);
|
|
|
|
rv = entry->write(address + offset, count, offset);
|
|
if (rv == -1)
|
|
{
|
|
perror(NULL);
|
|
return -1;
|
|
}
|
|
offset += count;
|
|
remaining -= count;
|
|
}
|
|
|
|
}
|
|
|
|
volume->sync();
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
Pascal::VolumeEntryPointer volume;
|
|
Device::BlockDevicePointer device;
|
|
|
|
unsigned fmt = 0;
|
|
|
|
int c;
|
|
|
|
|
|
#ifdef __linux__
|
|
putenv((char *)"POSIXLY_CORRECT=1"); // fix getopt to not mutate
|
|
#endif
|
|
|
|
|
|
|
|
// getop stops at first non '-' arg so it will not affect action flags.
|
|
while ((c = ::getopt(argc, argv, "f:h")) != -1)
|
|
{
|
|
std::printf("%c\n", c);
|
|
switch(c)
|
|
{
|
|
case 'f':
|
|
fmt = Device::BlockDevice::ImageType(optarg);
|
|
if (!fmt)
|
|
{
|
|
std::fprintf(stderr, "Error: Invalid file format: ``%s''.\n",
|
|
optarg);
|
|
}
|
|
break;
|
|
|
|
case 'h':
|
|
default:
|
|
usage();
|
|
return c == 'h' ? 0 : 1;
|
|
}
|
|
}
|
|
|
|
|
|
argc -= optind;
|
|
argv += optind;
|
|
|
|
#ifdef __linux__
|
|
optind = 0;
|
|
#else
|
|
optreset = 1;
|
|
optind = 1;
|
|
#endif
|
|
|
|
if (argc < 2)
|
|
{
|
|
usage();
|
|
return 0;
|
|
}
|
|
|
|
|
|
const char *file = argv[0];
|
|
const char *action = argv[1];
|
|
|
|
|
|
|
|
try {
|
|
|
|
unsigned actionCode = command(action);
|
|
|
|
device = Device::BlockDevice::Open(file, commandFlags(actionCode), fmt);
|
|
|
|
volume = Pascal::VolumeEntry::Open(device);
|
|
device.reset();
|
|
|
|
switch (actionCode)
|
|
{
|
|
case kCommandCAT:
|
|
return action_cat(argc - 1, argv + 1, volume.get());
|
|
break;
|
|
case kCommandCP:
|
|
return action_cp(argc - 1, argv + 1, volume.get());
|
|
break;
|
|
case kCommandKRUNCH:
|
|
return action_krunch(argc - 1, argv + 1, volume.get());
|
|
break;
|
|
case kCommandLS:
|
|
return action_ls(argc - 1, argv + 1, volume.get());
|
|
break;
|
|
case kCommandMV:
|
|
return action_mv(argc - 1, argv + 1, volume.get());
|
|
break;
|
|
case kCommandRM:
|
|
return action_rm(argc - 1, argv + 1, volume.get());
|
|
break;
|
|
case kCommandGET:
|
|
return action_get(argc -1, argv + 1, volume.get());
|
|
break;
|
|
case kCommandPUT:
|
|
return action_put(argc -1, argv + 1, volume.get());
|
|
break;
|
|
|
|
}
|
|
usage();
|
|
return 3;
|
|
}
|
|
catch (ProFUSE::Exception& e)
|
|
{
|
|
std::fprintf(stderr, "%s\n", e.what());
|
|
std::fprintf(stderr, "%s\n", e.errorString());
|
|
}
|
|
|
|
return 0;
|
|
}
|