xref: /openbsd/sbin/pdisk/pdisk.c (revision 17df1aa7)
1 //
2 // pdisk - an editor for Apple format partition tables
3 //
4 // Written by Eryk Vershen
5 //
6 // Still under development (as of 15 January 1998)
7 //
8 
9 /*
10  * Copyright 1996,1997,1998 by Apple Computer, Inc.
11  *              All Rights Reserved
12  *
13  * Permission to use, copy, modify, and distribute this software and
14  * its documentation for any purpose and without fee is hereby granted,
15  * provided that the above copyright notice appears in all copies and
16  * that both the copyright notice and this permission notice appear in
17  * supporting documentation.
18  *
19  * APPLE COMPUTER DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
20  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
21  * FOR A PARTICULAR PURPOSE.
22  *
23  * IN NO EVENT SHALL APPLE COMPUTER BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
24  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
25  * LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
26  * NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
27  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
28  */
29 
30 // for printf()
31 #include <stdio.h>
32 
33 // for malloc() & free()
34 #include <stdlib.h>
35 #include <unistd.h>
36 
37 // for strncpy() & strlen()
38 #include <string.h>
39 // for O_RDONLY
40 #include <fcntl.h>
41 // for errno
42 #include <errno.h>
43 
44 #include "pdisk.h"
45 #include "io.h"
46 #include "partition_map.h"
47 #include "pathname.h"
48 #include "hfs_misc.h"
49 #include "errors.h"
50 #include "dump.h"
51 #include "validate.h"
52 #include "version.h"
53 #include "util.h"
54 
55 
56 //
57 // Defines
58 //
59 #define ARGV_CHUNK 5
60 #define CFLAG_DEFAULT	0
61 #define DFLAG_DEFAULT	0
62 #define HFLAG_DEFAULT	0
63 #define INTERACT_DEFAULT	0
64 #define LFLAG_DEFAULT	0
65 #define RFLAG_DEFAULT	0
66 #define VFLAG_DEFAULT	0
67 
68 
69 //
70 // Types
71 //
72 
73 
74 //
75 // Global Constants
76 //
77 enum getopt_values {
78     kLongOption = 0,
79     kBadOption = '?',
80     kOptionArg = 1000,
81     kListOption = 1001,
82     kLogicalOption = 1002
83 };
84 
85 
86 //
87 // Global Variables
88 //
89 int lflag = LFLAG_DEFAULT;	/* list the device */
90 char *lfile;	/* list */
91 int vflag = VFLAG_DEFAULT;	/* show version */
92 int hflag = HFLAG_DEFAULT;	/* show help */
93 int dflag = DFLAG_DEFAULT;	/* turn on debugging commands and printout */
94 int rflag = RFLAG_DEFAULT;	/* open device read Only */
95 int interactive = INTERACT_DEFAULT;
96 int cflag = CFLAG_DEFAULT;	/* compute device size */
97 
98 static int first_get = 1;
99 
100 
101 //
102 // Forward declarations
103 //
104 void do_change_map_size(partition_map_header *map);
105 #ifdef __m68k__
106 void do_update_dpme(partition_map *entry);
107 #endif
108 void do_create_partition(partition_map_header *map, int get_type);
109 void do_delete_partition(partition_map_header *map);
110 void do_display_block(partition_map_header *map, char *alt_name);
111 void do_display_entry(partition_map_header *map);
112 void do_examine_patch_partition(partition_map_header *map);
113 int do_expert(partition_map_header *map, char *name);
114 void do_rename_partition(partition_map_header *map);
115 void do_change_type(partition_map_header *map);
116 void do_reorder(partition_map_header *map);
117 void do_write_partition_map(partition_map_header *map);
118 void edit(char *name, int ask_logical_size);
119 int get_base_argument(long *number, partition_map_header *map);
120 int get_command_line(int *argc, char ***argv);
121 int get_size_argument(long *number, partition_map_header *map);
122 int get_options(int argc, char **argv);
123 void interact(void);
124 void print_edit_notes(void);
125 void print_expert_notes(void);
126 
127 
128 //
129 // Routines
130 //
131 int
132 main(int argc, char **argv)
133 {
134     int name_index;
135     char *versionstr;
136 
137     init_program_name(argv);
138 
139     if (sizeof(DPME) != PBLOCK_SIZE) {
140 	fatal(-1, "Size of partition map entry (%d) "
141 		"is not equal to block size (%d)\n",
142 		sizeof(DPME), PBLOCK_SIZE);
143     }
144     if (sizeof(Block0) != PBLOCK_SIZE) {
145 	fatal(-1, "Size of block zero structure (%d) "
146 		"is not equal to block size (%d)\n",
147 		sizeof(Block0), PBLOCK_SIZE);
148     }
149     versionstr = (char *)get_version_string();
150     if (versionstr) {
151 	if (strcmp(VERSION, versionstr) != 0) {
152 		fatal(-1, "Version string static form (%s) does not match dynamic form (%s)\n",
153 		    VERSION, versionstr);
154 	}
155 	free(versionstr);
156     }
157 
158     name_index = get_options(argc, argv);
159 
160     if (vflag) {
161 	printf("version " VERSION " (" RELEASE_DATE ")\n");
162     }
163     if (hflag) {
164  	do_help();
165     } else if (interactive) {
166 	interact();
167     } else if (lflag) {
168 	if (lfile != NULL) {
169 	    dump(lfile);
170 	} else if (name_index < argc) {
171 	    while (name_index < argc) {
172 		dump(argv[name_index++]);
173 	    }
174 	} else {
175 	    usage("no device argument");
176 	    do_help();
177 	}
178     } else if (name_index < argc) {
179 	while (name_index < argc) {
180 	    edit(argv[name_index++], 0);
181 	}
182     } else if (!vflag) {
183 	usage("no device argument");
184  	do_help();
185     }
186     return 0;
187 }
188 
189 
190 void
191 interact()
192 {
193     char *name;
194     int command;
195     int ask_logical_size;
196 
197     while (get_command("Top level command (? for help): ", first_get, &command)) {
198 	first_get = 0;
199 	ask_logical_size = 0;
200 
201 	switch (command) {
202 	case '?':
203 	    // fall through
204 	case 'H':
205 	case 'h':
206 	    printf("Commands are:\n");
207 	    printf("  h    print help\n");
208 	    printf("  v    print the version number and release date\n");
209 	    printf("  l    list device's map\n");
210 	    printf("  e    edit device's map\n");
211 	    printf("  E    (edit map with specified block size)\n");
212 	    printf("  r    toggle readonly flag\n");
213 	    printf("  f    toggle show filesystem name flag\n");
214 	    if (dflag) {
215 		printf("  a    toggle abbreviate flag\n");
216 		printf("  p    toggle physical flag\n");
217 		printf("  c    toggle compute size flag\n");
218 		printf("  d    toggle debug flag\n");
219 		printf("  x    examine block n of device\n");
220 	    }
221 	    printf("  q    quit the program\n");
222 	    break;
223 	case 'Q':
224 	case 'q':
225 	    return;
226 	    break;
227 	case 'V':
228 	case 'v':
229 	    printf("version " VERSION " (" RELEASE_DATE ")\n");
230 	    break;
231 	case 'l':
232 	    if (get_string_argument("Name of device: ", &name, 1) == 0) {
233 		bad_input("Bad name");
234 		break;
235 	    }
236 	    dump(name);
237 	    free(name);
238 	    break;
239 	case 'E':
240 	    ask_logical_size = 1;
241 	case 'e':
242 	    if (get_string_argument("Name of device: ", &name, 1) == 0) {
243 		bad_input("Bad name");
244 		break;
245 	    }
246 	    edit(name, ask_logical_size);
247 	    free(name);
248 	    break;
249 	case 'R':
250 	case 'r':
251 	    if (rflag) {
252 		rflag = 0;
253 	    } else {
254 		rflag = 1;
255 	    }
256 	    printf("Now in %s mode.\n", (rflag)?"readonly":"read/write");
257 	    break;
258 	case 'F':
259 	case 'f':
260 	    if (fflag) {
261 		fflag = 0;
262 	    } else {
263 		fflag = 1;
264 	    }
265 	    printf("Now in show %s name mode.\n", (fflag)?"filesystem":"partition");
266 	    break;
267 	case 'A':
268 	case 'a':
269 	    if (dflag) {
270 		if (aflag) {
271 		    aflag = 0;
272 		} else {
273 		    aflag = 1;
274 		}
275 		printf("Now in %s mode.\n", (aflag)?"abbreviate":"full type");
276 	    } else {
277 	    	goto do_error;
278 	    }
279 	    break;
280 	case 'P':
281 	case 'p':
282 	    if (dflag) {
283 		if (pflag) {
284 		    pflag = 0;
285 		} else {
286 		    pflag = 1;
287 		}
288 		printf("Now in %s mode.\n", (pflag)?"physical":"logical");
289 	    } else {
290 	    	goto do_error;
291 	    }
292 	    break;
293 	case 'D':
294 	case 'd':
295 	    if (dflag) {
296 		dflag = 0;
297 	    } else {
298 		dflag = 1;
299 	    }
300 	    printf("Now in %s mode.\n", (dflag)?"debug":"normal");
301 	    break;
302 	case 'C':
303 	case 'c':
304 	    if (dflag) {
305 		if (cflag) {
306 		    cflag = 0;
307 		} else {
308 		    cflag = 1;
309 		}
310 		printf("Now in %s device size mode.\n", (cflag)?"always compute":"use existing");
311 	    } else {
312 	    	goto do_error;
313 	    }
314 	    break;
315 	case 'X':
316 	case 'x':
317 	    if (dflag) {
318 		do_display_block(0, 0);
319 	    } else {
320 	    	goto do_error;
321 	    }
322 	    break;
323 	default:
324 	do_error:
325 	    bad_input("No such command (%c)", command);
326 	    break;
327 	}
328     }
329 }
330 
331 
332 int
333 get_options(int argc, char **argv)
334 {
335     int c;
336     extern int optind;
337     extern char *optarg;
338     int flag = 0;
339 
340     lflag = LFLAG_DEFAULT;
341     lfile = NULL;
342     vflag = VFLAG_DEFAULT;
343     hflag = HFLAG_DEFAULT;
344     dflag = DFLAG_DEFAULT;
345     rflag = RFLAG_DEFAULT;
346     aflag = AFLAG_DEFAULT;
347     pflag = PFLAG_DEFAULT;
348     interactive = INTERACT_DEFAULT;
349     cflag = CFLAG_DEFAULT;
350 
351     optind = 1; // reset option scanner logic
352     while ((c = getopt(argc, argv, "hlvdric")) != -1) {
353 	switch (c) {
354 	case 'h':
355 	    hflag = (HFLAG_DEFAULT)?0:1;
356 	    break;
357 	case 'l':
358 	    lflag = (LFLAG_DEFAULT)?0:1;
359 	    break;
360 	case 'v':
361 	    vflag = (VFLAG_DEFAULT)?0:1;
362 	    break;
363 	case 'd':
364 	    dflag = (DFLAG_DEFAULT)?0:1;
365 	    break;
366 	case 'c':
367 	    cflag = (CFLAG_DEFAULT)?0:1;
368 	    break;
369 	case 'r':
370 	    rflag = (RFLAG_DEFAULT)?0:1;
371 	    break;
372 	case 'i':
373 	    interactive = (INTERACT_DEFAULT)?0:1;
374 	    break;
375 	case 'a':
376 	    aflag = (AFLAG_DEFAULT)?0:1;
377 	    break;
378 	case kLogicalOption:
379 	    pflag = (PFLAG_DEFAULT)?0:1;
380 	    break;
381 	default:
382 	    flag = 1;
383 	    break;
384 	}
385     }
386     if (flag) {
387 	usage("bad arguments");
388     }
389     return optind;
390 }
391 
392 
393 void
394 print_edit_notes()
395 {
396     printf("Notes:\n");
397     printf("  Base and length fields are blocks, which vary in size between media.\n");
398     printf("  The base field can be <nth>p; i.e. use the base of the nth partition.\n");
399     printf("  The length field can be a length followed by k, m, g or t to indicate\n");
400     printf("  kilo, mega, giga, or tera bytes; also the length can be <nth>p; i.e. use\n");
401     printf("  the length of the nth partition.\n");
402     printf("  The name of a partition is descriptive text.\n");
403     printf("\n");
404 }
405 
406 
407 //
408 // Edit the file
409 //
410 void
411 edit(char *name, int ask_logical_size)
412 {
413     partition_map_header *map;
414     int command;
415     int order;
416     int get_type;
417     int valid_file;
418 
419     map = open_partition_map(name, &valid_file, ask_logical_size);
420     if (!valid_file) {
421     	return;
422     }
423 
424     printf("Edit %s -\n", name);
425 
426     while (get_command("Command (? for help): ", first_get, &command)) {
427 	first_get = 0;
428 	order = 1;
429 	get_type = 0;
430 
431 	switch (command) {
432 	case '?':
433 	    print_edit_notes();
434 	    // fall through
435 	case 'H':
436 	case 'h':
437 	    printf("Commands are:\n");
438 	    printf("  C    (create with type also specified)\n");
439 	    printf("  c    create new partition (standard OpenBSD root)\n");
440 	    printf("  d    delete a partition\n");
441 	    printf("  h    help\n");
442 	    printf("  i    initialize partition map\n");
443 	    printf("  n    (re)name a partition\n");
444 	    printf("  P    (print ordered by base address)\n");
445 	    printf("  p    print the partition table\n");
446 	    printf("  q    quit editing\n");
447 	    printf("  r    reorder partition entry in map\n");
448 	    printf("  s    change size of partition map\n");
449 	    printf("  t    change a partition's type\n");
450 	    if (!rflag) {
451 		printf("  w    write the partition table\n");
452 	    }
453 	    if (dflag) {
454 		printf("  x    extra extensions for experts\n");
455 	    }
456 	    break;
457 	case 'P':
458 	    order = 0;
459 	    // fall through
460 	case 'p':
461 	    dump_partition_map(map, order);
462 	    break;
463 	case 'Q':
464 	case 'q':
465 	    if (map && map->changed) {
466 		if (get_okay("Discard changes? [n/y]: ", 0) != 1) {
467 		    break;
468 		}
469 	    }
470 	    flush_to_newline(1);
471 	    goto finis;
472 	    break;
473 	case 'I':
474 	case 'i':
475 	    map = init_partition_map(name, map);
476 	    break;
477 	case 'C':
478 	    get_type = 1;
479 	    // fall through
480 	case 'c':
481 	    do_create_partition(map, get_type);
482 	    break;
483 	case 'N':
484 	case 'n':
485 	    do_rename_partition(map);
486 	    break;
487 	case 'D':
488 	case 'd':
489 	    do_delete_partition(map);
490 	    break;
491 	case 'R':
492 	case 'r':
493 	    do_reorder(map);
494 	    break;
495 	case 'S':
496 	case 's':
497 	    do_change_map_size(map);
498 	    break;
499 	case 'T':
500 	case 't':
501 	    do_change_type(map);
502 	    break;
503 	case 'X':
504 	case 'x':
505 	    if (!dflag) {
506 		goto do_error;
507 	    } else if (do_expert(map, name)) {
508 		flush_to_newline(1);
509 		goto finis;
510 	    }
511 	    break;
512 	case 'W':
513 	case 'w':
514 	    if (!rflag) {
515 		do_write_partition_map(map);
516 	    } else {
517 	    	goto do_error;
518 	    }
519 	    break;
520 	default:
521 	do_error:
522 	    bad_input("No such command (%c)", command);
523 	    break;
524 	}
525     }
526 finis:
527 
528     close_partition_map(map);
529 }
530 
531 #ifdef __m68k__
532 void
533 do_update_dpme(partition_map *entry)
534 {
535     int slice = 0;
536     if (!entry) return;
537     dpme_init_flags(entry->data);
538     entry->HFS_name = get_HFS_name(entry, &entry->HFS_kind);
539     if (istrncmp(entry->data->dpme_type, kUnixType, DPISTRLEN) == 0) {
540 	printf("Available partition slices for %s:\n",entry->data->dpme_type);
541 	printf("  a   root partition\n");
542 	printf("  b   swap partition\n");
543 	printf("  c   do not set any bzb bits\n");
544 	printf("  g   user partition\n");
545 	printf("Other lettered values will create user partitions\n");
546 	get_command("Select a slice for default bzb values: ",0,&slice);
547     }
548     bzb_init_slice((BZB *)entry->data->dpme_bzb,slice);
549     entry->the_map->changed = 1;
550 }
551 #endif
552 
553 void
554 do_create_partition(partition_map_header *map, int get_type)
555 {
556     long base;
557     long length;
558     char *name = 0;
559     char *type_name = 0;
560 
561     if (map == NULL) {
562 	bad_input("No partition map exists");
563 	return;
564     }
565     if (!rflag && map->writable == 0) {
566 	printf("The map is not writable.\n");
567     }
568 // XXX add help feature (i.e. '?' in any argument routine prints help string)
569     if (get_base_argument(&base, map) == 0) {
570 	return;
571     }
572     if (get_size_argument(&length, map) == 0) {
573 	return;
574     }
575 
576     if (get_string_argument("Name of partition: ", &name, 1) == 0) {
577 	bad_input("Bad name");
578 	return;
579     }
580     if (get_type == 0) {
581 	add_partition_to_map(name, kUnixType, base, length, map);
582     } else if (get_string_argument("Type of partition: ", &type_name, 1) == 0) {
583 	bad_input("Bad type");
584 	goto xit1;
585     } else {
586 	if (istrncmp(type_name, kFreeType, DPISTRLEN) == 0) {
587 	    bad_input("Can't create a partition with the Free type");
588 	    goto xit2;
589 	}
590 	if (istrncmp(type_name, kMapType, DPISTRLEN) == 0) {
591 	    bad_input("Can't create a partition with the Map type");
592 	    goto xit2;
593 	}
594 	add_partition_to_map(name, type_name, base, length, map);
595     }
596 #ifdef __m68k__
597     do_update_dpme(find_entry_by_base(base,map));
598 #endif
599 xit2:
600     if (type_name)
601         free(type_name);
602 xit1:
603     if (name)
604         free(name);
605     return;
606 }
607 
608 
609 int
610 get_base_argument(long *number, partition_map_header *map)
611 {
612     partition_map * entry;
613     int result = 0;
614 
615     if (get_number_argument("First block: ", number, kDefault) == 0) {
616 	bad_input("Bad block number");
617     } else {
618 	result = 1;
619 	if (get_partition_modifier()) {
620 	    entry = find_entry_by_disk_address(*number, map);
621 	    if (entry == NULL) {
622 		bad_input("Bad partition number");
623 		result = 0;
624 	    } else {
625 		*number = entry->data->dpme_pblock_start;
626 	    }
627 	}
628     }
629     return result;
630 }
631 
632 
633 int
634 get_size_argument(long *number, partition_map_header *map)
635 {
636     partition_map * entry;
637     int result = 0;
638     unsigned long multiple;
639 
640     if (get_number_argument("Length in blocks: ", number, kDefault) == 0) {
641 	bad_input("Bad length");
642     } else {
643 	multiple = get_multiplier(map->logical_block);
644 	if (multiple == 0) {
645 	    bad_input("Bad multiplier");
646 	} else if (multiple != 1) {
647 	    *number *= multiple;
648 	    result = 1;
649 	} else if (get_partition_modifier()) {
650 	    entry = find_entry_by_disk_address(*number, map);
651 	    if (entry == NULL) {
652 		bad_input("Bad partition number");
653 	    } else {
654 		*number = entry->data->dpme_pblocks;
655 		result = 1;
656 	    }
657 	} else {
658 	    result = 1;
659 	}
660     }
661     return result;
662 }
663 
664 
665 void
666 do_rename_partition(partition_map_header *map)
667 {
668     partition_map * entry;
669     long ix;
670     char *name;
671 
672     if (map == NULL) {
673 	bad_input("No partition map exists");
674 	return;
675     }
676     if (!rflag && map->writable == 0) {
677 	printf("The map is not writable.\n");
678     }
679     if (get_number_argument("Partition number: ", &ix, kDefault) == 0) {
680 	bad_input("Bad partition number");
681 	return;
682     }
683     if (get_string_argument("New name of partition: ", &name, 1) == 0) {
684 	bad_input("Bad name");
685 	return;
686     }
687 
688 	// find partition and change it
689     entry = find_entry_by_disk_address(ix, map);
690     if (entry == NULL) {
691 	printf("No such partition\n");
692     } else {
693 	// stuff name into partition map entry data
694 	strncpy(entry->data->dpme_name, name, DPISTRLEN);
695 	map->changed = 1;
696     }
697     free(name);
698     return;
699 }
700 
701 void
702 do_change_type(partition_map_header *map)
703 {
704     partition_map * entry;
705     long ix;
706     char *type = NULL;
707 
708     if (map == NULL) {
709 	bad_input("No partition map exists");
710 	return;
711     }
712 
713     if (!rflag && map->writable == 0) {
714 	printf("The map is not writable.\n");
715     }
716 
717     if (get_number_argument("Partition number: ", &ix, kDefault) == 0) {
718 	bad_input("Bad partition number");
719 	return;
720     }
721 
722     entry = find_entry_by_disk_address(ix, map);
723 
724     if (entry == NULL ) {
725         printf("No such partition\n");
726 	goto out;
727     }
728 
729     printf("Existing partition type ``%s''.\n", entry->data->dpme_type);
730     if (get_string_argument("New type of partition: ", &type, 1) == 0) {
731 	bad_input("Bad type");
732 	goto out;
733     }
734 
735     strncpy(entry->data->dpme_type, type, DPISTRLEN);
736 #ifdef __m68k__
737     do_update_dpme(entry);
738 #endif
739     map->changed = 1;
740 
741 out:
742     if (type)
743         free(type);
744     return;
745 }
746 
747 
748 void
749 do_delete_partition(partition_map_header *map)
750 {
751     partition_map * cur;
752     long ix;
753 
754     if (map == NULL) {
755 	bad_input("No partition map exists");
756 	return;
757     }
758     if (!rflag && map->writable == 0) {
759 	printf("The map is not writable.\n");
760     }
761     if (get_number_argument("Partition number: ", &ix, kDefault) == 0) {
762 	bad_input("Bad partition number");
763 	return;
764     }
765 
766 	// find partition and delete it
767     cur = find_entry_by_disk_address(ix, map);
768     if (cur == NULL) {
769 	printf("No such partition\n");
770     } else {
771 	delete_partition_from_map(cur);
772     }
773 }
774 
775 
776 void
777 do_reorder(partition_map_header *map)
778 {
779     long old_index;
780     long ix;
781 
782     if (map == NULL) {
783 	bad_input("No partition map exists");
784 	return;
785     }
786     if (!rflag && map->writable == 0) {
787 	printf("The map is not writable.\n");
788     }
789     if (get_number_argument("Partition number: ", &old_index, kDefault) == 0) {
790 	bad_input("Bad partition number");
791 	return;
792     }
793     if (get_number_argument("New number: ", &ix, kDefault) == 0) {
794 	bad_input("Bad partition number");
795 	return;
796     }
797 
798     move_entry_in_map(old_index, ix, map);
799 }
800 
801 
802 void
803 do_write_partition_map(partition_map_header *map)
804 {
805     if (map == NULL) {
806 	bad_input("No partition map exists");
807 	return;
808     }
809     if (map->changed == 0 && map->written == 0) {
810 	bad_input("The map has not been changed.");
811 	return;
812     }
813     if (map->writable == 0) {
814 	bad_input("The map is not writable.");
815 	return;
816     }
817     printf("Writing the map destroys what was there before. ");
818     if (get_okay("Is that okay? [n/y]: ", 0) != 1) {
819 	return;
820     }
821 
822     write_partition_map(map);
823 
824     map->changed = 0;
825     map->written = 1;
826 
827     // exit(0);
828 }
829 
830 
831 void
832 print_expert_notes()
833 {
834     printf("Notes:\n");
835     printf("  The expert commands are for low level and experimental features.\n");
836     printf("  These commands are available only when debug mode is on.\n");
837     printf("\n");
838 }
839 
840 
841 int
842 do_expert(partition_map_header *map, char *name)
843 {
844     int command;
845     int quit = 0;
846 
847     while (get_command("Expert command (? for help): ", first_get, &command)) {
848 	first_get = 0;
849 
850 	switch (command) {
851 	case '?':
852 	    print_expert_notes();
853 	    // fall through
854 	case 'H':
855 	case 'h':
856 	    printf("Commands are:\n");
857 	    printf("  h    print help\n");
858 	    printf("  d    dump block n\n");
859 	    printf("  p    print the partition table\n");
860 	    if (dflag) {
861 		printf("  P    (show data structures  - debugging)\n");
862 	    }
863 	    printf("  f    full display of nth entry\n");
864 	    printf("  v    validate map\n");
865 	    printf("  e    examine patch partition\n");
866 	    printf("  q    return to main edit menu\n");
867 	    printf("  Q    quit editing\n");
868 	    break;
869 	case 'q':
870 	    flush_to_newline(1);
871 	    goto finis;
872 	    break;
873 	case 'Q':
874 	    if (map->changed) {
875 		if (get_okay("Discard changes? [n/y]: ", 0) != 1) {
876 		    break;
877 		}
878 	    }
879 	    quit = 1;
880 	    goto finis;
881 	    break;
882 	case 'P':
883 	    if (dflag) {
884 		show_data_structures(map);
885 		break;
886 	    }
887 	    // fall through
888 	case 'p':
889 	    dump_partition_map(map, 1);
890 	    break;
891 	case 'D':
892 	case 'd':
893 	    do_display_block(map, name);
894 	    break;
895 	case 'F':
896 	case 'f':
897 	    do_display_entry(map);
898 	    break;
899 	case 'V':
900 	case 'v':
901 	    validate_map(map);
902 	    break;
903 	case 'E':
904 	case 'e':
905 	    do_examine_patch_partition(map);
906 	    break;
907 	default:
908 	    bad_input("No such command (%c)", command);
909 	    break;
910 	}
911     }
912 finis:
913     return quit;
914 }
915 
916 void
917 do_change_map_size(partition_map_header *map)
918 {
919     long size;
920 
921     if (map == NULL) {
922 	bad_input("No partition map exists");
923 	return;
924     }
925     if (!rflag && map->writable == 0) {
926 	printf("The map is not writable.\n");
927     }
928     if (get_number_argument("New size: ", &size, kDefault) == 0) {
929 	bad_input("Bad size");
930 	return;
931     }
932     resize_map(size, map);
933 }
934 
935 
936 void
937 do_display_block(partition_map_header *map, char *alt_name)
938 {
939     MEDIA m;
940     long number;
941     char *name;
942     static unsigned char *display_block;
943     static int display_g;
944     int g;
945     static long next_number = -1;
946 
947     if (map != NULL) {
948     	name = 0;
949 	m = map->m;
950 	g = map->logical_block;
951     } else {
952 	if (alt_name == 0) {
953 	    if (get_string_argument("Name of device: ", &name, 1) == 0) {
954 		bad_input("Bad name");
955 		return;
956 	    }
957 	} else {
958 	    if ((name = strdup(alt_name)) == NULL) {
959 		error(errno, "strdup failed");
960 		return;
961 	    }
962 	}
963 	m = open_pathname_as_media(name, O_RDONLY);
964 	if (m == 0) {
965 	    error(errno, "can't open file '%s'", name);
966 	    free(name);
967 	    return;
968 	}
969 	g = media_granularity(m);
970 	if (g < PBLOCK_SIZE) {
971 	    g = PBLOCK_SIZE;
972 	}
973     }
974     if (get_number_argument("Block number: ", &number, next_number) == 0) {
975 	bad_input("Bad block number");
976 	goto xit;
977     }
978     if (display_block == NULL || display_g < g) {
979     	if (display_block != NULL) {
980     	    free(display_block);
981     	    display_g = 0;
982 	}
983 	display_block = (unsigned char *) malloc(g);
984 	if (display_block == NULL) {
985 	    error(errno, "can't allocate memory for display block buffer");
986 	    goto xit;
987 	}
988 	display_g = g;
989     }
990     if (read_media(m, ((long long)number) * g, g, (char *)display_block) != 0) {
991 	printf("block %ld -", number);
992 	dump_block((unsigned char*) display_block, g);
993 	next_number = number + 1;
994     }
995 
996 xit:
997     if (name) {
998 	close_media(m);
999 	free(name);
1000     }
1001     return;
1002 }
1003 
1004 
1005 void
1006 do_display_entry(partition_map_header *map)
1007 {
1008     long number;
1009 
1010     if (map == NULL) {
1011 	bad_input("No partition map exists");
1012 	return;
1013     }
1014     if (get_number_argument("Partition number: ", &number, kDefault) == 0) {
1015 	bad_input("Bad partition number");
1016 	return;
1017     }
1018     if (number == 0) {
1019     	full_dump_block_zero(map);
1020     } else {
1021 	full_dump_partition_entry(map, number);
1022     }
1023 }
1024 
1025 
1026 void
1027 do_examine_patch_partition(partition_map_header *map)
1028 {
1029     partition_map * entry;
1030 
1031     if (map == NULL) {
1032 	bad_input("No partition map exists");
1033 	return;
1034     }
1035     entry = find_entry_by_type(kPatchType, map);
1036     if (entry == NULL) {
1037 	printf("No patch partition\n");
1038     } else {
1039 	display_patches(entry);
1040     }
1041 }
1042