xref: /haiku/src/tests/apps/partitioner/Partitioner.cpp (revision 52c4471a3024d2eb81fe88e2c3982b9f8daa5e56)
1 /*
2  * Copyright 2007, Haiku, Inc. All Rights Reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Ingo Weinhold <bonefish@cs.tu-berlin.de>
7  */
8 
9 #include <ctype.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 
14 #include <DiskDevice.h>
15 #include <DiskDeviceRoster.h>
16 #include <DiskDeviceVisitor.h>
17 #include <DiskSystem.h>
18 #include <PartitioningInfo.h>
19 #include <Path.h>
20 #include <String.h>
21 
22 #include <ObjectList.h>
23 
24 
25 extern "C" const char* __progname;
26 static const char* kProgramName = __progname;
27 
28 static const char* kUsage =
29 "Usage: %s <options> <device>\n"
30 "\n"
31 "Options:\n"
32 "  -h, --help   - print this help text\n"
33 ;
34 
35 
36 static void
37 print_usage(bool error)
38 {
39 	fprintf(error ? stderr : stdout, kUsage, kProgramName);
40 }
41 
42 
43 static void
44 print_usage_and_exit(bool error)
45 {
46 	print_usage(error);
47 	exit(error ? 1 : 0);
48 }
49 
50 
51 static void
52 get_size_string(off_t _size, BString& string)
53 {
54 	const char* suffixes[] = {
55 		"", "K", "M", "G", "T", NULL
56 	};
57 
58 	double size = _size;
59 	int index = 0;
60 	while (size > 1024 && suffixes[index + 1]) {
61 		size /= 1024;
62 		index++;
63 	}
64 
65 	char buffer[128];
66 	snprintf(buffer, sizeof(buffer), "%.2f%s", size, suffixes[index]);
67 
68 	string = buffer;
69 }
70 
71 
72 // PrintLongVisitor
73 class PrintLongVisitor : public BDiskDeviceVisitor {
74 public:
75 	virtual bool Visit(BDiskDevice *device)
76 	{
77 		BPath path;
78 		status_t error = device->GetPath(&path);
79 		const char *pathString = NULL;
80 		if (error == B_OK)
81 			pathString = path.Path();
82 		else
83 			pathString = strerror(error);
84 		printf("device %" B_PRId32 ": \"%s\"\n", device->ID(), pathString);
85 		printf("  has media:      %d\n", device->HasMedia());
86 		printf("  removable:      %d\n", device->IsRemovableMedia());
87 		printf("  read only:      %d\n", device->IsReadOnlyMedia());
88 		printf("  write once:     %d\n", device->IsWriteOnceMedia());
89 		printf("  ---\n");
90 		Visit(device, 0);
91 		return false;
92 	}
93 
94 	virtual bool Visit(BPartition *partition, int32 level)
95 	{
96 		char prefix[128];
97 		sprintf(prefix, "%*s", 2 * (int)level, "");
98 		if (level > 0) {
99 			BPath path;
100 			status_t error = partition->GetPath(&path);
101 			const char *pathString = NULL;
102 			if (error == B_OK)
103 				pathString = path.Path();
104 			else
105 				pathString = strerror(error);
106 			printf("%spartition %" B_PRId32 ": \"%s\"\n", prefix, partition->ID(),
107 				   pathString);
108 		}
109 		printf("%s  offset:         %" B_PRId64 "\n", prefix, partition->Offset());
110 		printf("%s  size:           %" B_PRId64 "\n", prefix, partition->Size());
111 		printf("%s  block size:     %" B_PRIu32 "\n", prefix, partition->BlockSize());
112 		printf("%s  index:          %" B_PRId32 "\n", prefix, partition->Index());
113 		printf("%s  status:         %" B_PRIu32 "\n", prefix, partition->Status());
114 		printf("%s  file system:    %d\n", prefix,
115 			   partition->ContainsFileSystem());
116 		printf("%s  part. system:   %d\n", prefix,
117 			   partition->ContainsPartitioningSystem());
118 		printf("%s  device:         %d\n", prefix, partition->IsDevice());
119 		printf("%s  read only:      %d\n", prefix, partition->IsReadOnly());
120 		printf("%s  mounted:        %d\n", prefix, partition->IsMounted());
121 		printf("%s  flags:          %" B_PRIx32 "\n", prefix, partition->Flags());
122 		printf("%s  name:           \"%s\"\n", prefix, partition->Name());
123 		printf("%s  content name:   \"%s\"\n", prefix,
124 			partition->ContentName().String());
125 		printf("%s  type:           \"%s\"\n", prefix, partition->Type());
126 		printf("%s  content type:   \"%s\"\n", prefix,
127 			partition->ContentType());
128 		printf("%s  params:         \"%s\"\n", prefix, partition->Parameters());
129 		printf("%s  content params: \"%s\"\n", prefix,
130 			partition->ContentParameters());
131 		// volume, icon,...
132 		return false;
133 	}
134 };
135 
136 
137 static void
138 print_partition_table_header()
139 {
140 	printf("   Index     Start      Size                Content Type      "
141 		"Content Name\n");
142 	printf("--------------------------------------------------------------"
143 		"------------\n");
144 }
145 
146 
147 static void
148 print_partition(BPartition* partition, int level, int index)
149 {
150 	BString offset, size;
151 	get_size_string(partition->Offset(), offset);
152 	get_size_string(partition->Size(), size);
153 
154 	printf("%*s%02d%*s  %8s  %8s  %26.26s  %16.16s\n", 2 * level, "",
155 		index,
156 		2 * max_c(3 - level, 0), "",
157 		offset.String(), size.String(),
158 		(partition->ContentType() ? partition->ContentType() : "-"),
159 		partition->ContentName().String());
160 }
161 
162 
163 // PrintShortVisitor
164 class PrintShortVisitor : public BDiskDeviceVisitor {
165 public:
166 	virtual bool Visit(BDiskDevice* device)
167 	{
168 		fIndex = 0;
169 
170 		// get path
171 		BPath path;
172 		status_t error = device->GetPath(&path);
173 		const char *pathString = NULL;
174 		if (error == B_OK)
175 			pathString = path.Path();
176 		else
177 			pathString = strerror(error);
178 
179 		// check media present; if so which read/write abilities
180 		const char* media;
181 		const char* readWrite;
182 		if (device->HasMedia()) {
183 			if (device->IsRemovableMedia()) {
184 				media = "removable media";
185 			} else {
186 				media = "fixed media";
187 			}
188 
189 			if (device->IsReadOnlyMedia()) {
190 				if (device->IsWriteOnceMedia())
191 					readWrite = ", write once";
192 				else
193 					readWrite = ", read-only";
194 			} else
195 				readWrite = ", read/write";
196 		} else {
197 			media = "no media";
198 			readWrite = "";
199 		}
200 
201 		printf("\ndevice %" B_PRId32 ": \"%s\": %s%s\n\n", device->ID(), pathString,
202 			media, readWrite);
203 		print_partition_table_header();
204 
205 		Visit(device, 0);
206 		return false;
207 	}
208 
209 	virtual bool Visit(BPartition* partition, int32 level)
210 	{
211 		print_partition(partition, level, fIndex++);
212 		return false;
213 	}
214 
215 private:
216 	int	fIndex;
217 };
218 
219 
220 // FindPartitionByIndexVisitor
221 class FindPartitionByIndexVisitor : public BDiskDeviceVisitor {
222 public:
223 	FindPartitionByIndexVisitor(int index)
224 		: fIndex(index)
225 	{
226 	}
227 
228 	virtual bool Visit(BDiskDevice *device)
229 	{
230 		return Visit(device, 0);
231 	}
232 
233 	virtual bool Visit(BPartition *partition, int32 level)
234 	{
235 		return (fIndex-- == 0);
236 	}
237 
238 private:
239 	int	fIndex;
240 };
241 
242 
243 // Partitioner
244 class Partitioner {
245 public:
246 	Partitioner(BDiskDevice* device)
247 		: fDevice(device),
248 		  fPrepared(false)
249 	{
250 	}
251 
252 	void Run()
253 	{
254 		// prepare device modifications
255 		if (fDevice->IsReadOnly()) {
256 			printf("Device is read-only. Can't change anything.\n");
257 		} else {
258 			status_t error = fDevice->PrepareModifications();
259 			fPrepared = (error == B_OK);
260 			if (error != B_OK) {
261 				printf("Error: Failed to prepare device for modifications: "
262 					"%s\n", strerror(error));
263 			}
264 		}
265 
266 		// main input loop
267 		while (true) {
268 			BString line;
269 			if (!_ReadLine("party> ", line))
270 				return;
271 
272 			if (line == "") {
273 				// do nothing
274 			} else if (line == "h" || line == "help") {
275 				_PrintHelp();
276 			} else if (line == "i") {
277 				_InitializePartition();
278 			} else if (line == "l") {
279 				_PrintPartitionsShort();
280 			} else if (line == "ll") {
281 				_PrintPartitionsLong();
282 			} else if (line == "n") {
283 				_NewPartition();
284 			} else if (line == "q" || line == "quit") {
285 				return;
286 			} else if (line == "w") {
287 				_WriteChanges();
288 			} else {
289 				printf("Invalid command \"%s\", type \"help\" for help\n",
290 					line.String());
291 			}
292 		}
293 	}
294 
295 private:
296 	void _PrintHelp()
297 	{
298 		printf("Valid commands:\n"
299 			"  h, help  - prints this help text\n"
300 			"  i        - initialize a partition\n"
301 			"  l        - lists the device's partitions recursively\n"
302 			"  ll       - lists the device's partitions recursively, long "
303 				"format\n"
304 			"  l        - create a new child partition\n"
305 			"  q, quit  - quits the program, changes are discarded\n"
306 			"  w        - write changes to disk\n");
307 	}
308 
309 	void _PrintPartitionsShort()
310 	{
311 		PrintShortVisitor visitor;
312 		fDevice->VisitEachDescendant(&visitor);
313 	}
314 
315 	void _PrintPartitionsLong()
316 	{
317 		PrintLongVisitor visitor;
318 		fDevice->VisitEachDescendant(&visitor);
319 	}
320 
321 	void _InitializePartition()
322 	{
323 		if (!fPrepared) {
324 			if (fDevice->IsReadOnly())
325 				printf("Device is read-only!\n");
326 			else
327 				printf("Sorry, not prepared for modifications!\n");
328 			return;
329 		}
330 
331 		// get the partition
332 		int32 partitionIndex;
333 		BPartition* partition = NULL;
334 		if (!_SelectPartition("partition index [-1 to abort]: ", partition,
335 				partitionIndex)) {
336 			return;
337 		}
338 
339 		printf("\nselected partition:\n\n");
340 		print_partition_table_header();
341 		print_partition(partition, 0, partitionIndex);
342 
343 		// get available disk systems
344 		BObjectList<BDiskSystem> diskSystems(20, true);
345 		BDiskDeviceRoster roster;
346 		{
347 			BDiskSystem diskSystem;
348 			while (roster.GetNextDiskSystem(&diskSystem) == B_OK) {
349 				if (partition->CanInitialize(diskSystem.PrettyName()))
350 					diskSystems.AddItem(new BDiskSystem(diskSystem));
351 			}
352 		}
353 
354 		if (diskSystems.IsEmpty()) {
355 			printf("There are no disk systems, that can initialize this "
356 				"partition.\n");
357 			return;
358 		}
359 
360 		// print the available disk systems
361 		printf("\ndisk systems that can initialize the selected partition:\n");
362 		for (int32 i = 0; BDiskSystem* diskSystem = diskSystems.ItemAt(i); i++)
363 			printf("%2" B_PRId32 "  %s\n", i, diskSystem->PrettyName());
364 
365 		printf("\n");
366 
367 		// get the disk system
368 		int64 diskSystemIndex;
369 		if (!_ReadNumber("disk system index [-1 to abort]: ", 0,
370 				diskSystems.CountItems() - 1, -1, "invalid index",
371 				diskSystemIndex)) {
372 			return;
373 		}
374 		BDiskSystem* diskSystem = diskSystems.ItemAt(diskSystemIndex);
375 
376 		bool supportsName = diskSystem->SupportsContentName();
377 		BString name;
378 		BString parameters;
379 		while (true) {
380 			// let the user enter name and parameters
381 			if ((supportsName && !_ReadLine("partition name: ", name))
382 				|| !_ReadLine("partition parameters: ", parameters)) {
383 				return;
384 			}
385 
386 			// validate parameters
387 			BString validatedName(name);
388 			if (partition->ValidateInitialize(diskSystem->PrettyName(),
389 					supportsName ? &validatedName : NULL, parameters.String())
390 					!= B_OK) {
391 				printf("Validation of the given values failed. Sorry, can't "
392 					"continue.\n");
393 				return;
394 			}
395 
396 			// did the disk system change the name?
397 			if (!supportsName || name == validatedName) {
398 				printf("Everything looks dandy.\n");
399 			} else {
400 				printf("The disk system adjusted the file name to \"%s\".\n",
401 					validatedName.String());
402 				name = validatedName;
403 			}
404 
405 			// let the user decide whether to continue, change parameters, or
406 			// abort
407 			bool changeParameters = false;
408 			while (true) {
409 				BString line;
410 				_ReadLine("[c]ontinue, change [p]arameters, or [a]bort? ", line);
411 				if (line == "a")
412 					return;
413 				if (line == "p") {
414 					changeParameters = true;
415 					break;
416 				}
417 				if (line == "c")
418 					break;
419 
420 				printf("invalid input\n");
421 			}
422 
423 			if (!changeParameters)
424 				break;
425 		}
426 
427 		// initialize
428 		status_t error = partition->Initialize(diskSystem->PrettyName(),
429 			supportsName ? name.String() : NULL, parameters.String());
430 		if (error != B_OK)
431 			printf("Initialization failed: %s\n", strerror(error));
432 	}
433 
434 	void _NewPartition()
435 	{
436 		if (!fPrepared) {
437 			if (fDevice->IsReadOnly())
438 				printf("Device is read-only!\n");
439 			else
440 				printf("Sorry, not prepared for modifications!\n");
441 			return;
442 		}
443 
444 		// get the parent partition
445 		BPartition* partition = NULL;
446 		int32 partitionIndex;
447 		if (!_SelectPartition("parent partition index [-1 to abort]: ",
448 				partition, partitionIndex)) {
449 			return;
450 		}
451 
452 		printf("\nselected partition:\n\n");
453 		print_partition_table_header();
454 		print_partition(partition, 0, partitionIndex);
455 
456 		if (!partition->ContainsPartitioningSystem()) {
457 			printf("The selected partition does not contain a partitioning "
458 				"system.\n");
459 			return;
460 		}
461 
462 		// get supported types
463 		BObjectList<BString> supportedTypes(20, true);
464 		BString typeBuffer;
465 		int32 cookie = 0;
466 		while (partition->GetNextSupportedChildType(&cookie, &typeBuffer)
467 				== B_OK) {
468 			supportedTypes.AddItem(new BString(typeBuffer));
469 		}
470 
471 		if (supportedTypes.IsEmpty()) {
472 			printf("The partitioning system is not able to create any "
473 				"child partition (no supported types).\n");
474 			return;
475 		}
476 
477 		// get partitioning info
478 		BPartitioningInfo partitioningInfo;
479 		status_t error = partition->GetPartitioningInfo(&partitioningInfo);
480 		if (error != B_OK) {
481 			printf("Failed to get partitioning info for partition: %s\n",
482 				strerror(error));
483 			return;
484 		}
485 
486 		int32 spacesCount = partitioningInfo.CountPartitionableSpaces();
487 		if (spacesCount == 0) {
488 			printf("There's no space on the partition where a child partition "
489 				"could be created\n");
490 			return;
491 		}
492 
493 		// let the user select the partition type, if there's more than one
494 		int64 typeIndex = 0;
495 		int32 supportedTypesCount = supportedTypes.CountItems();
496 		if (supportedTypesCount > 1) {
497 			// list them
498 			printf("Possible partition types:\n");
499 			for (int32 i = 0; i < supportedTypesCount; i++)
500 				printf("%2" B_PRId32 "  %s\n", i, supportedTypes.ItemAt(i)->String());
501 
502 			if (!_ReadNumber("supported type index [-1 to abort]: ", 0,
503 					supportedTypesCount - 1, -1, "invalid index", typeIndex)) {
504 				return;
505 			}
506 		}
507 
508 		const char* type = supportedTypes.ItemAt(typeIndex)->String();
509 
510 		// list partitionable spaces
511 		printf("Unused regions where the new partition could be created:\n");
512 		for (int32 i = 0; i < spacesCount; i++) {
513 			off_t _offset;
514 			off_t _size;
515 			partitioningInfo.GetPartitionableSpaceAt(i, &_offset, &_size);
516 			BString offset, size;
517 			get_size_string(_offset, offset);
518 			get_size_string(_size, size);
519 			printf("%2" B_PRId32 "  start: %8s,  size:  %8s\n", i, offset.String(),
520 				size.String());
521 		}
522 
523 		// let the user select the partitionable space, if there's more than one
524 		int64 spaceIndex = 0;
525 		if (spacesCount > 1) {
526 			if (!_ReadNumber("unused region index [-1 to abort]: ", 0,
527 					spacesCount - 1, -1, "invalid index", spaceIndex)) {
528 				return;
529 			}
530 		}
531 
532 		off_t spaceOffset;
533 		off_t spaceSize;
534 		partitioningInfo.GetPartitionableSpaceAt(spaceIndex, &spaceOffset,
535 			&spaceSize);
536 
537 		off_t start;
538 		off_t size;
539 		BString parameters;
540 		while (true) {
541 			// let the user enter start, size, and parameters
542 
543 			// start
544 			while (true) {
545 				BString spaceOffsetString;
546 				get_size_string(spaceOffset, spaceOffsetString);
547 				BString prompt("partition start [default: ");
548 				prompt << spaceOffsetString << "]: ";
549 				if (!_ReadSize(prompt.String(), spaceOffset, start))
550 					return;
551 
552 				if (start >= spaceOffset && start <= spaceOffset + spaceSize)
553 					break;
554 
555 				printf("invalid partition start\n");
556 			}
557 
558 			// size
559 			off_t maxSize = spaceOffset + spaceSize - start;
560 			while (true) {
561 				BString maxSizeString;
562 				get_size_string(maxSize, maxSizeString);
563 				BString prompt("partition size [default: ");
564 				prompt << maxSizeString << "]: ";
565 				if (!_ReadSize(prompt.String(), maxSize, size))
566 					return;
567 
568 				if (size >= 0 && start + size <= spaceOffset + spaceSize)
569 					break;
570 
571 				printf("invalid partition size\n");
572 			}
573 
574 			// parameters
575 			if (!_ReadLine("partition parameters: ", parameters))
576 				return;
577 
578 			// validate parameters
579 			off_t validatedStart = start;
580 			off_t validatedSize = size;
581 // TODO: Support the name parameter!
582 			if (partition->ValidateCreateChild(&start, &size, type, NULL,
583 					parameters.String()) != B_OK) {
584 				printf("Validation of the given values failed. Sorry, can't "
585 					"continue.\n");
586 				return;
587 			}
588 
589 			// did the disk system change offset or size?
590 			if (validatedStart == start && validatedSize == size) {
591 				printf("Everything looks dandy.\n");
592 			} else {
593 				BString startString, sizeString;
594 				get_size_string(validatedStart, startString);
595 				get_size_string(validatedSize, sizeString);
596 				printf("The disk system adjusted the partition start and "
597 					"size to %" B_PRIdOFF " (%s) and %" B_PRIdOFF " (%s).\n",
598 					validatedStart, startString.String(), validatedSize,
599 					sizeString.String());
600 				start = validatedStart;
601 				size = validatedSize;
602 			}
603 
604 			// let the user decide whether to continue, change parameters, or
605 			// abort
606 			bool changeParameters = false;
607 			while (true) {
608 				BString line;
609 				_ReadLine("[c]ontinue, change [p]arameters, or [a]bort? ", line);
610 				if (line == "a")
611 					return;
612 				if (line == "p") {
613 					changeParameters = true;
614 					break;
615 				}
616 				if (line == "c")
617 					break;
618 
619 				printf("invalid input\n");
620 			}
621 
622 			if (!changeParameters)
623 				break;
624 		}
625 
626 		// create child
627 		error = partition->CreateChild(start, size, type, NULL,
628 			parameters.String());
629 		if (error != B_OK)
630 			printf("Creating the partition failed: %s\n", strerror(error));
631 	}
632 
633 	void _WriteChanges()
634 	{
635 		if (!fPrepared || !fDevice->IsModified()) {
636 			printf("No changes have been made!\n");
637 			return;
638 		}
639 
640 		printf("Writing changes to disk. This can take a while...\n");
641 
642 		// commit
643 		status_t error = fDevice->CommitModifications();
644 		if (error == B_OK) {
645 			printf("All changes have been written successfully!\n");
646 		} else {
647 			printf("Failed to write all changes: %s\n", strerror(error));
648 		}
649 
650 		// prepare again
651 		error = fDevice->PrepareModifications();
652 		fPrepared = (error == B_OK);
653 		if (error != B_OK) {
654 			printf("Error: Failed to prepare device for modifications: "
655 				"%s\n", strerror(error));
656 		}
657 	}
658 
659 	bool _SelectPartition(const char* prompt, BPartition*& partition,
660 		int32& _partitionIndex)
661 	{
662 		// if the disk device has no children, we select it without asking
663 		if (fDevice->CountChildren() == 0) {
664 			partition = fDevice;
665 			_partitionIndex = 0;
666 			return true;
667 		}
668 
669 		// otherwise let the user select
670 		_PrintPartitionsShort();
671 
672 		partition = NULL;
673 		int64 partitionIndex;
674 		while (true) {
675 			if (!_ReadNumber(prompt, partitionIndex) || partitionIndex < 0)
676 				return false;
677 
678 			FindPartitionByIndexVisitor visitor(partitionIndex);
679 			partition = fDevice->VisitEachDescendant(&visitor);
680 			if (partition) {
681 				_partitionIndex = partitionIndex;
682 				return true;
683 			}
684 
685 			printf("invalid partition index\n");
686 		}
687 	}
688 
689 	bool _ReadLine(const char* prompt, BString& _line)
690 	{
691 		// prompt
692 		printf(prompt);
693 		fflush(stdout);
694 
695 		// read line
696 		char line[256];
697 		if (!fgets(line, sizeof(line), stdin))
698 			return false;
699 
700 		// remove trailing '\n'
701 		if (char* trailingNL = strchr(line, '\n'))
702 			*trailingNL = '\0';
703 
704 		_line = line;
705 		return true;
706 	}
707 
708 	bool _ReadNumber(const char* prompt, int64& number)
709 	{
710 		while (true) {
711 			BString line;
712 			if (!_ReadLine(prompt, line))
713 				return false;
714 
715 			char buffer[256];
716 			if (sscanf(line.String(), "%" B_PRId64 "%s", &number, buffer) == 1)
717 				return true;
718 
719 			printf("invalid input\n");
720 		}
721 	}
722 
723 	bool _ReadNumber(const char* prompt, int64 minNumber, int64 maxNumber,
724 		int64 abortNumber, const char* invalidNumberMessage, int64& number)
725 	{
726 		while (true) {
727 			BString line;
728 			if (!_ReadLine(prompt, line))
729 				return false;
730 
731 			char buffer[256];
732 			if (sscanf(line.String(), "%" B_PRId64 "%s", &number, buffer) != 1) {
733 				printf("invalid input\n");
734 				continue;
735 			}
736 
737 			if (number == abortNumber)
738 				return false;
739 
740 			if (number >= minNumber && number <= maxNumber)
741 				return true;
742 
743 			puts(invalidNumberMessage);
744 		}
745 	}
746 
747 	bool _ReadSize(const char* prompt, off_t defaultValue, off_t& size)
748 	{
749 		while (true) {
750 			BString _line;
751 			if (!_ReadLine(prompt, _line))
752 				return false;
753 			const char* line = _line.String();
754 
755 			// skip whitespace
756 			while (isspace(*line))
757 				line++;
758 
759 			if (*line == '\0') {
760 				size = defaultValue;
761 				return true;
762 			}
763 
764 			// get the number
765 			int32 endIndex = 0;
766 			while (isdigit(line[endIndex]))
767 				endIndex++;
768 
769 			if (endIndex == 0) {
770 				printf("invalid input\n");
771 				continue;
772 			}
773 
774 			size = atoll(BString(line, endIndex).String());
775 
776 			// skip whitespace
777 			line += endIndex;
778 			while (isspace(*line))
779 				line++;
780 
781 			// get the size modifier
782 			if (*line != '\0') {
783 				switch (*line) {
784 					case 'K':
785 						size *= 1024;
786 						break;
787 					case 'M':
788 						size *= 1024 * 1024;
789 						break;
790 					case 'G':
791 						size *= 1024LL * 1024 * 1024;
792 						break;
793 					case 'T':
794 						size *= 1024LL * 1024 * 1024 * 1024;
795 						break;
796 					default:
797 						line--;
798 				}
799 
800 				line++;
801 			}
802 
803 			if (*line == 'B')
804 				line++;
805 
806 			// skip whitespace
807 			while (isspace(*line))
808 				line++;
809 
810 			if (*line == '\0')
811 				return true;
812 
813 			printf("invalid input\n");
814 		}
815 	}
816 
817 private:
818 	BDiskDevice*	fDevice;
819 	bool			fPrepared;
820 };
821 
822 
823 int
824 main(int argc, const char* const* argv)
825 {
826 	// parse arguments
827 	int argi = 1;
828 	for (; argi < argc; argi++) {
829 		const char* arg = argv[argi];
830 		if (arg[0] == '-') {
831 			if (arg[1] == '-') {
832 				// a double '-' option
833 				if (strcmp(arg, "--help") == 0) {
834 					print_usage_and_exit(false);
835 				} else
836 					print_usage_and_exit(true);
837 			} else {
838 				// single '-' options
839 				for (int i = 1; arg[i] != '\0'; i++) {
840 					switch (arg[i]) {
841 						case 'h':
842 							print_usage_and_exit(false);
843 						default:
844 							print_usage_and_exit(true);
845 					}
846 				}
847 			}
848 		} else
849 			break;
850 	}
851 
852 	// only the device path should remain
853 	if (argi != argc - 1)
854 		print_usage_and_exit(true);
855 	const char* devicePath = argv[argi];
856 
857 	// get the disk device
858 	BDiskDeviceRoster roster;
859 	BDiskDevice device;
860 	status_t error = roster.GetDeviceForPath(devicePath, &device);
861 	if (error != B_OK) {
862 		fprintf(stderr, "Error: Failed to get disk device for path \"%s\": "
863 			"%s\n", devicePath, strerror(error));
864 	}
865 
866 	Partitioner partitioner(&device);
867 	partitioner.Run();
868 
869 	return 0;
870 }
871