25 using std::unique_ptr;
29 #ifndef _MSC_EXTENSIONS
32 const unsigned DiskManipulator::MAX_PARTITIONS;
37 :
Command(commandController,
"diskmanipulator")
44 assert(drives.empty());
47 string DiskManipulator::getMachinePrefix()
const
50 return id.empty() ?
id :
id +
"::";
56 assert(findDriveSettings(drive) == drives.end());
57 DriveSettings driveSettings;
58 driveSettings.drive = &drive;
60 driveSettings.partition = 0;
61 for (
unsigned i = 0; i <= MAX_PARTITIONS; ++i) {
62 driveSettings.workingDir[i] =
'/';
64 drives.push_back(driveSettings);
69 auto it = findDriveSettings(drive);
70 assert(it != drives.end());
74 DiskManipulator::Drives::iterator DiskManipulator::findDriveSettings(
77 return find_if(drives.begin(), drives.end(),
78 [&](DriveSettings& ds) {
return ds.drive == &drive; });
81 DiskManipulator::Drives::iterator DiskManipulator::findDriveSettings(
84 return find_if(drives.begin(), drives.end(),
85 [&](DriveSettings& ds) {
return ds.driveName == name; });
88 DiskManipulator::DriveSettings& DiskManipulator::getDriveSettings(
93 auto pos1 = diskname.
find(
"::");
97 auto tmp2 = diskname.
substr(0, pos2 + pos1b);
99 auto it = findDriveSettings(tmp2);
100 if (it == drives.end()) {
101 it = findDriveSettings(getMachinePrefix() + tmp2);
102 if (it == drives.end()) {
103 throw CommandException(
"Unknown drive: " + tmp2);
107 auto* disk = it->drive->getSectorAccessibleDisk();
110 throw CommandException(
"Unsupported disk type.");
117 const auto& num = diskname.
substr(pos2);
125 unique_ptr<DiskPartition> DiskManipulator::getPartition(
126 const DriveSettings& driveData)
128 auto* disk = driveData.drive->getSectorAccessibleDisk();
130 return make_unique<DiskPartition>(*disk, driveData.partition);
134 string DiskManipulator::execute(
const vector<string>& tokens)
137 if (tokens.size() == 1) {
138 throw CommandException(
"Missing argument");
140 }
else if ((tokens.size() != 4 && ( tokens[1] ==
"savedsk"
141 || tokens[1] ==
"mkdir"))
142 || (tokens.size() != 3 && (tokens[1] ==
"dir"
143 || tokens[1] ==
"format"))
144 || ((tokens.size() < 3 || tokens.size() > 4) &&
145 (tokens[1] ==
"chdir"))
146 || (tokens.size() < 4 && ( tokens[1] ==
"export"
147 || tokens[1] ==
"import"))
148 || (tokens.size() <= 3 && (tokens[1] ==
"create"))) {
149 throw CommandException(
"Incorrect number of parameters");
151 }
else if ( tokens[1] ==
"export" ) {
153 throw CommandException(tokens[3] +
" is not a directory");
155 auto& settings = getDriveSettings(tokens[2]);
156 vector<string> lists(tokens.begin() + 4, tokens.end());
157 exprt(settings, tokens[3], lists);
159 }
else if (tokens[1] ==
"import" ) {
160 auto& settings = getDriveSettings(tokens[2]);
161 vector<string> lists(tokens.begin() + 3, tokens.end());
162 result =
import(settings, lists);
164 }
else if (tokens[1] ==
"savedsk") {
165 auto& settings = getDriveSettings(tokens[2]);
166 savedsk(settings, tokens[3]);
168 }
else if (tokens[1] ==
"chdir") {
169 auto& settings = getDriveSettings(tokens[2]);
170 if (tokens.size() == 3) {
171 result +=
"Current directory: " +
172 settings.workingDir[settings.partition];
174 result += chdir(settings, tokens[3]);
177 }
else if (tokens[1] ==
"mkdir") {
178 auto& settings = getDriveSettings(tokens[2]);
179 mkdir(settings, tokens[3]);
181 }
else if (tokens[1] ==
"create") {
184 }
else if (tokens[1] ==
"format") {
185 auto& settings = getDriveSettings(tokens[2]);
188 }
else if (tokens[1] ==
"dir") {
189 auto& settings = getDriveSettings(tokens[2]);
190 result += dir(settings);
193 throw CommandException(
"Unknown subcommand: " + tokens[1]);
198 string DiskManipulator::help(
const vector<string>& tokens)
const
201 if (tokens.size() >= 2) {
202 if (tokens[1] ==
"import" ) {
204 "diskmanipulator import <disk name> <host directory|host file>\n"
205 "Import all files and subdirs from the host OS as specified into the\n"
206 "<disk name> in the current MSX subdirectory as was specified with the\n"
207 "last chdir command.\n";
208 }
else if (tokens[1] ==
"export" ) {
210 "diskmanipulator export <disk name> <host directory>\n"
211 "Extract all files and subdirs from the MSX subdirectory specified with\n"
212 "the chdir command from <disk name> to the host OS in <host directory>.\n";
213 }
else if (tokens[1] ==
"savedsk") {
215 "diskmanipulator savedsk <disk name> <dskfilename>\n"
216 "This saves the complete drive content to <dskfilename>, it is not possible to\n"
217 "save just one partition. The main purpose of this command is to make it\n"
218 "possible to save a 'ramdsk' into a file and to take 'live backups' of\n"
219 "dsk-files in use.\n";
220 }
else if (tokens[1] ==
"chdir") {
222 "diskmanipulator chdir <disk name> <MSX directory>\n"
223 "Change the working directory on <disk name>. This will be the\n"
224 "directory were the 'import', 'export' and 'dir' commands will\n"
226 "In case of a partitioned drive, each partition has its own\n"
227 "working directory.\n";
228 }
else if (tokens[1] ==
"mkdir") {
230 "diskmanipulator mkdir <disk name> <MSX directory>\n"
231 "This creates the directory on <disk name>. If needed, all missing\n"
232 "parent directories are created at the same time. Accepts both\n"
233 "absolute and relative pathnames.\n";
234 }
else if (tokens[1] ==
"create") {
236 "diskmanipulator create <dskfilename> <size/option> [<size/option>...]\n"
237 "Creates a formatted dsk file with the given size.\n"
238 "If multiple sizes are given, a partitioned disk image will\n"
239 "be created with each partition having the size as indicated. By\n"
240 "default the sizes are expressed in kilobyte, add the postfix M\n"
242 }
else if (tokens[1] ==
"format") {
244 "diskmanipulator format <disk name>\n"
245 "formats the current (partition on) <disk name> with a regular\n"
246 "FAT12 MSX filesystem with an MSX-DOS2 boot sector.\n";
247 }
else if (tokens[1] ==
"dir") {
249 "diskmanipulator dir <disk name>\n"
250 "Shows the content of the current directory on <disk name>\n";
252 helptext =
"Unknown diskmanipulator subcommand: " + tokens[1];
256 "diskmanipulator create <fn> <sz> [<sz> ...] : create a formatted dsk file with name <fn>\n"
257 " having the given (partition) size(s)\n"
258 "diskmanipulator savedsk <disk name> <fn> : save <disk name> as dsk file named as <fn>\n"
259 "diskmanipulator format <disk name> : format (a partition) on <disk name>\n"
260 "diskmanipulator chdir <disk name> <MSX dir> : change directory on <disk name>\n"
261 "diskmanipulator mkdir <disk name> <MSX dir> : create directory on <disk name>\n"
262 "diskmanipulator dir <disk name> : long format file listing of current\n"
263 " directory on <disk name>\n"
264 "diskmanipulator import <disk> <dir/file> ... : import files and subdirs from <dir/file>\n"
265 "diskmanipulator export <disk> <host dir> : export all files on <disk> to <host dir>\n"
266 "For more info use 'help diskmanipulator <subcommand>'.\n";
271 void DiskManipulator::tabCompletion(vector<string>& tokens)
const
273 if (tokens.size() == 2) {
274 static const char*
const cmds[] = {
275 "import",
"export",
"savedsk",
"dir",
"create",
276 "format",
"chdir",
"mkdir",
280 }
else if ((tokens.size() == 3) && (tokens[1] ==
"create")) {
283 }
else if (tokens.size() == 3) {
284 vector<string> names;
285 for (
auto& d : drives) {
286 const auto& name1 = d.driveName;
287 const auto& name2 = d.drive->getContainerName();
288 names.push_back(name1);
289 names.push_back(name2);
292 if (
auto* disk = d.drive->getSectorAccessibleDisk()) {
293 for (
unsigned i = 1; i <= MAX_PARTITIONS; ++i) {
298 }
catch (MSXException&) {
306 }
else if (tokens.size() >= 4) {
307 if ((tokens[1] ==
"savedsk") ||
308 (tokens[1] ==
"import") ||
309 (tokens[1] ==
"export")) {
311 }
else if (tokens[1] ==
"create") {
312 static const char*
const cmds[] = {
320 void DiskManipulator::savedsk(
const DriveSettings& driveData,
321 const string& filename)
323 auto partition = getPartition(driveData);
332 void DiskManipulator::create(
const vector<string>& tokens)
334 vector<unsigned> sizes;
335 unsigned totalSectors = 0;
336 for (
unsigned i = 3; i < tokens.size(); ++i) {
337 if (sizes.size() >= MAX_PARTITIONS) {
339 "Maximum number of partitions is " << MAX_PARTITIONS);
342 int sectors = strtol(tokens[i].c_str(), &q, 0);
345 if ((q == tokens[i].c_str()) || *(q + 1)) {
346 throw CommandException(
347 "Invalid size: " + tokens[i]);
349 switch (tolower(*q)) {
363 throw CommandException(
364 string(
"Invalid postfix: ") + q);
371 if (sectors > 65535) sectors = 65535;
378 if (sectors < 720) sectors = 720;
380 sizes.push_back(sectors);
381 totalSectors += sectors;
383 if (sizes.size() > 1) {
393 }
catch (FileException& e) {
394 throw CommandException(
"Couldn't create image: " + e.getMessage());
398 DSKDiskImage image(filename);
399 if (sizes.size() > 1) {
407 void DiskManipulator::format(DriveSettings& driveData)
409 auto partition = getPartition(driveData);
411 driveData.workingDir[driveData.partition] =
'/';
414 unique_ptr<MSXtar> DiskManipulator::getMSXtar(
415 SectorAccessibleDisk& disk, DriveSettings& driveData)
418 throw CommandException(
419 "Please select partition number.");
422 auto result = make_unique<MSXtar>(disk);
424 result->chdir(driveData.workingDir[driveData.partition]);
425 }
catch (MSXException&) {
426 driveData.workingDir[driveData.partition] =
'/';
427 throw CommandException(
428 "Directory " + driveData.workingDir[driveData.partition] +
429 " doesn't exist anymore. Went back to root "
430 "directory. Command aborted, please retry.");
435 string DiskManipulator::dir(DriveSettings& driveData)
437 auto partition = getPartition(driveData);
438 unique_ptr<MSXtar> workhorse = getMSXtar(*
partition, driveData);
439 return workhorse->dir();
442 string DiskManipulator::chdir(DriveSettings& driveData,
const string& filename)
444 auto partition = getPartition(driveData);
445 auto workhorse = getMSXtar(*
partition, driveData);
447 workhorse->chdir(filename);
448 }
catch (MSXException& e) {
449 throw CommandException(
"chdir failed: " + e.getMessage());
452 string& cwd = driveData.workingDir[driveData.partition];
459 return "New working directory: " + cwd;
462 void DiskManipulator::mkdir(DriveSettings& driveData,
const string& filename)
464 auto partition = getPartition(driveData);
465 auto workhorse = getMSXtar(*
partition, driveData);
467 workhorse->mkdir(filename);
468 }
catch (MSXException& e) {
469 throw CommandException(e.getMessage());
473 string DiskManipulator::import(DriveSettings& driveData,
474 const vector<string>& lists)
476 auto partition = getPartition(driveData);
477 auto workhorse = getMSXtar(*
partition, driveData);
480 for (
auto& l : lists) {
485 throw CommandException(
486 "Non-existing file " + i);
489 messages += workhorse->addDir(i);
491 messages += workhorse->addFile(i);
494 messages +=
"Ignoring " + i +
'\n';
496 }
catch (MSXException& e) {
497 throw CommandException(e.getMessage());
504 void DiskManipulator::exprt(DriveSettings& driveData,
const string& dirname,
505 const vector<string>& lists)
507 auto partition = getPartition(driveData);
508 auto workhorse = getMSXtar(*
partition, driveData);
512 workhorse->getDir(dirname);
514 for (
auto& l : lists) {
515 workhorse->getItemFromDir(dirname, l);
518 }
catch (MSXException& e) {
519 throw CommandException(e.getMessage());