1 /* part 1.57 - Partition table editor Author: Kees J. Bot
3 * Needs about 22k heap+stack.
21 #include <sys/ioctl.h>
22 #include <minix/config.h>
23 #include <minix/const.h>
24 #include <minix/partition.h>
25 #include <machine/partition.h>
28 /* True if a partition is an extended partition. */
29 #define ext_part(s) ((s) == 0x05 || (s) == 0x0F)
31 /* Minix master bootstrap code. */
32 static char MASTERBOOT
[] = "/usr/mdec/mbr";
35 ----first---- --geom/last-- ------sectors-----
36 Device Cyl Head Sec Cyl Head Sec Base Size Kb
38 /dev/c0d0:2 0 0 2 976 4 16 2 83043 41521
40 0* p0 81 MINIX 0 0 3 33 4 9 3 2880 1440
41 1 p1 81 MINIX 33 4 10 178 2 2 2883 12284 6142
42 2 p2 81 MINIX 178 2 3 976 4 16 15167 67878 33939
43 3 p3 00 None 0 0 0 0 0 -1 0 0 0
46 #define MAXSIZE 999999999L /* Will 1T be enough this year? */
47 #define SECTOR_SIZE 512
48 #define DEV_FD0 0x200 /* Device number of /dev/fd0 */
49 #define DEV_C0D0 0x300 /* Device number of /dev/c0d0 */
51 #define arraysize(a) (sizeof(a) / sizeof((a)[0]))
52 #define arraylimit(a) ((a) + arraysize(a))
54 void report(const char *label
)
56 fprintf(stderr
, "part: %s: %s\n", label
, strerror(errno
));
59 void fatal(const char *label
)
65 struct termios termios
;
67 void save_ttyflags(void)
68 /* Save tty attributes for later restoration. */
70 if (tcgetattr(0, &termios
) < 0) fatal("");
73 void restore_ttyflags(void)
74 /* Reset the tty flags to how we got 'em. */
76 if (tcsetattr(0, TCSANOW
, &termios
) < 0) fatal("");
80 /* Set the terminal to raw mode, no signals, no echoing. */
82 struct termios rawterm
;
85 rawterm
.c_lflag
&= ~(ICANON
|ISIG
|ECHO
);
86 rawterm
.c_iflag
&= ~(ICRNL
);
87 if (tcsetattr(0, TCSANOW
, &rawterm
) < 0) fatal("");
90 #define ctrl(c) ((c) == '?' ? '\177' : ((c) & '\37'))
92 char t_cd
[16], t_cm
[32], t_so
[16], t_se
[16], t_md
[16], t_me
[16];
97 /* Get terminal capabilities and set the tty to "editor" mode. */
100 static char termbuf
[1024];
103 if ((term
= getenv("TERM")) == nil
|| tgetent(termbuf
, term
) != 1) {
104 fprintf(stderr
, "part: Can't get terminal capabilities\n");
107 if (tgetstr("cd", (tp
= t_cd
, &tp
)) == nil
108 || tgetstr("cm", (tp
= t_cm
, &tp
)) == nil
) {
109 fprintf(stderr
, "part: This terminal is too dumb\n");
114 (void) tgetstr("so", (tp
= t_so
, &tp
));
115 (void) tgetstr("se", (tp
= t_se
, &tp
));
116 (void) tgetstr("md", (tp
= t_md
, &tp
));
117 (void) tgetstr("me", (tp
= t_me
, &tp
));
132 while ((c
= *s
++) != 0) putchr(c
);
135 void set_cursor(int row
, int col
)
137 tputs(tgoto(t_cm
, col
, row
), 1, putchr
);
140 int statusrow
= STATUSROW
;
144 void stat_start(int serious
)
145 /* Prepare for printing on a fresh status line, possibly highlighted. */
147 set_cursor(statusrow
++, 0);
148 tputs(t_cd
, 1, putchr
);
149 if (serious
) tputs(t_so
, 1, putchr
);
152 void stat_end(int ktl
)
153 /* Closing bracket for stat_start. Sets "keystrokes to live" of message. */
155 tputs(t_se
, 1, putchr
);
160 void stat_reset(void)
161 /* Reset the statusline pointer and clear old messages if expired. */
163 if (stat_ktl
> 0 && --stat_ktl
== 0) {
164 statusrow
= STATUSROW
;
167 if (need_help
&& statusrow
< (24-2)) {
168 if (statusrow
> STATUSROW
) stat_start(0);
171 "Type '+' or '-' to change, 'r' to read, '?' for more help, 'q' to exit");
173 statusrow
= STATUSROW
;
177 void clear_screen(void)
180 tputs(t_cd
, 1, putchr
);
186 /* Reset the tty to cooked mode. */
189 set_cursor(statusrow
, 0);
190 tputs(t_cd
, 1, putchr
);
193 void *alloc(size_t n
)
197 if ((m
= malloc(n
)) == nil
) { reset_tty(); fatal(""); }
202 typedef enum parttype
{ DUNNO
, SUBPART
, PRIMARY
, FLOPPY
} parttype_t
;
204 typedef struct device
{
205 struct device
*next
, *prev
; /* Circular dequeue. */
206 dev_t rdev
; /* Device number (sorting only). */
207 char *name
; /* E.g. /dev/c0d0 */
208 char *subname
; /* E.g. /dev/c0d0:2 */
212 device_t
*firstdev
= nil
, *curdev
;
214 void newdevice(char *name
, int scanning
)
215 /* Add a device to the device list. If scanning is set then we are reading
216 * /dev, so insert the device in device number order and make /dev/c0d0 current.
219 device_t
*new, *nextdev
, *prevdev
;
224 if (stat(name
, &st
) < 0 || !S_ISBLK(st
.st_mode
)) return;
226 switch (major(st
.st_rdev
)) {
229 if (minor(st
.st_rdev
) >= 4) return;
236 /* Disk controller */
237 if (minor(st
.st_rdev
) >= 0x80
238 || minor(st
.st_rdev
) % 5 != 0) return;
243 /* Interesting device found. */
245 (void) stat(name
, &st
);
248 new= alloc(sizeof(*new));
249 new->rdev
= st
.st_rdev
;
250 new->name
= alloc((strlen(name
) + 1) * sizeof(new->name
[0]));
251 strcpy(new->name
, name
);
252 new->subname
= new->name
;
253 new->parttype
= DUNNO
;
254 if (major(st
.st_rdev
) == major(DEV_FD0
) && minor(st
.st_rdev
) < 112) {
255 new->parttype
= FLOPPY
;
257 if (st
.st_rdev
>= DEV_C0D0
&& minor(st
.st_rdev
) < 128
258 && minor(st
.st_rdev
) % 5 == 0) {
259 new->parttype
= PRIMARY
;
262 if (firstdev
== nil
) {
264 new->next
= new->prev
= new;
269 while (new->rdev
>= nextdev
->rdev
270 && (nextdev
= nextdev
->next
) != firstdev
) {}
271 prevdev
= nextdev
->prev
;
277 if (new->rdev
< firstdev
->rdev
) firstdev
= new;
278 if (new->rdev
== DEV_C0D0
) curdev
= new;
279 if (curdev
->rdev
!= DEV_C0D0
) curdev
= firstdev
;
282 void getdevices(void)
283 /* Get all block devices from /dev that look interesting. */
287 char name
[5 + NAME_MAX
+ 1];
289 if ((d
= opendir("/dev")) == nil
) fatal("/dev");
291 while ((e
= readdir(d
)) != nil
) {
292 strcpy(name
, "/dev/");
293 strcpy(name
+ 5, e
->d_name
);
300 unsigned char bootblock
[SECTOR_SIZE
];
301 struct part_entry table
[1 + NR_PARTITIONS
];
302 int existing
[1 + NR_PARTITIONS
];
303 unsigned long offset
= 0, extbase
= 0, extsize
;
305 char sort_index
[1 + NR_PARTITIONS
];
306 unsigned cylinders
= 1, heads
= 1, sectors
= 1, secpcyl
= 1;
307 unsigned alt_cyls
= 1, alt_heads
= 1, alt_secs
= 1;
311 unsigned long sortbase(struct part_entry
*pe
)
313 return pe
->sysind
== NO_PART
? -1 : pe
->lowsec
;
317 /* Let the sort_index array show the order partitions are sorted in. */
320 int idx
[1 + NR_PARTITIONS
];
322 for (i
= 1; i
<= NR_PARTITIONS
; i
++) idx
[i
]= i
;
324 for (i
= 1; i
<= NR_PARTITIONS
; i
++) {
325 for (j
= 1; j
<= NR_PARTITIONS
-1; j
++) {
326 int sj
= idx
[j
], sj1
= idx
[j
+1];
328 if (sortbase(&table
[sj
]) > sortbase(&table
[sj1
])) {
334 for (i
= 1; i
<= NR_PARTITIONS
; i
++) sort_index
[idx
[i
]]= i
;
337 void dos2chs(unsigned char *dos
, unsigned *chs
)
338 /* Extract cylinder, head and sector from the three bytes DOS uses to address
339 * a sector. Note that bits 8 & 9 of the cylinder number come from bit 6 & 7
340 * of the sector byte. The sector number is rebased to count from 0.
343 chs
[0]= ((dos
[1] & 0xC0) << 2) | dos
[2];
345 chs
[2]= (dos
[1] & 0x3F) - 1;
348 void abs2dos(unsigned char *dos
, unsigned long pos
)
349 /* Translate a sector offset to three DOS bytes. */
354 h
= (pos
% secpcyl
) / sectors
;
355 s
= pos
% sectors
+ 1;
358 dos
[1]= s
| ((c
>> 2) & 0xC0);
362 void recompute0(void)
363 /* Recompute the partition size for the device after a geometry change. */
366 cylinders
= heads
= sectors
= 1;
367 memset(table
, 0, sizeof(table
));
369 if (!precise
&& offset
== 0) {
371 table
[0].size
= (unsigned long) cylinders
* heads
* sectors
;
373 table
[0].sysind
= device
< 0 ? NO_PART
: MINIX_PART
;
374 secpcyl
= heads
* sectors
;
377 void guess_geometry(void)
378 /* With a bit of work one can deduce the disk geometry from the partition
379 * table. This may be necessary if the driver gets it wrong. (If partition
380 * tables didn't have C/H/S numbers we would not care at all...)
384 struct part_entry
*pe
;
388 unsigned char HS
[256][8]; /* Bit map off all possible H/S */
390 alt_cyls
= alt_heads
= alt_secs
= 0;
392 /* Initially all possible H/S combinations are possible. HS[h][0]
393 * bit 0 is used to rule out a head value.
395 for (h
= 1; h
<= 255; h
++) {
396 for (s
= 0; s
< 8; s
++) HS
[h
][s
]= 0xFF;
399 for (i
= 0; i
< 2*NR_PARTITIONS
; i
++) {
400 pe
= &(table
+1)[i
>> 1];
401 if (pe
->sysind
== NO_PART
) continue;
403 /* Get the end or start sector numbers (in that order). */
405 dos2chs(&pe
->last_head
, chs
);
406 sec
= pe
->lowsec
+ pe
->size
- 1;
408 dos2chs(&pe
->start_head
, chs
);
412 if (chs
[0] >= alt_cyls
) alt_cyls
= chs
[0]+1;
414 /* Which H/S combinations can be ruled out? */
415 for (h
= 1; h
<= 255; h
++) {
416 if (HS
[h
][0] == 0) continue;
418 for (s
= 1; s
<= 63; s
++) {
419 if ((chs
[0] * h
+ chs
[1]) * s
+ chs
[2] != sec
) {
420 HS
[h
][s
/8] &= ~(1 << (s
%8));
422 if (HS
[h
][s
/8] & (1 << (s
%8))) n
++;
424 if (n
== 0) HS
[h
][0]= 0;
428 /* See if only one remains. */
430 for (h
= 1; h
<= 255; h
++) {
431 if (HS
[h
][0] == 0) continue;
432 for (s
= 1; s
<= 63; s
++) {
433 if (HS
[h
][s
/8] & (1 << (s
%8))) {
441 /* Forget it if more than one choice... */
442 if (i
> 1) alt_cyls
= alt_heads
= alt_secs
= 0;
446 /* Find out the geometry of the device by querying the driver, or by looking
447 * at the partition table. These numbers are crosschecked to make sure that
448 * the geometry is correct. Master bootstraps other than the Minix one use
449 * the CHS numbers in the partition table to load the bootstrap of the active
455 struct part_geom geometry
;
458 /* Geometry already known. */
465 if (device
< 0) return;
467 /* Try to guess the geometry from the partition table. */
470 /* Try to get the geometry from the driver. */
471 (void) fstat(device
, &dst
);
473 if (S_ISBLK(dst
.st_mode
) || S_ISCHR(dst
.st_mode
)) {
474 /* Try to get the drive's geometry from the driver. */
476 if (ioctl(device
, DIOCGETP
, &geometry
) < 0)
479 table
[0].lowsec
= (unsigned long)(geometry
.base
/
481 table
[0].size
= (unsigned long)(geometry
.size
/
483 cylinders
= geometry
.cylinders
;
484 heads
= geometry
.heads
;
485 sectors
= geometry
.sectors
;
493 /* Getting the geometry from the driver failed, so use the
494 * alternate geometry.
496 if (alt_heads
== 0) {
497 alt_cyls
= table
[0].size
/ (64 * 32);
507 printf("Failure to get the geometry of %s: %s", curdev
->name
,
508 errno
== ENOTTY
? "No driver support" : strerror(err
));
511 printf("The geometry has been guessed as %ux%ux%u",
512 cylinders
, heads
, sectors
);
515 if (alt_heads
== 0) {
521 if (heads
!= alt_heads
|| sectors
!= alt_secs
) {
527 "The %ux%ux%u geometry obtained from the device driver does not match",
528 cylinders
, heads
, sectors
);
532 "the %ux%ux%u geometry implied by the partition table. Hit 'X' to switch",
533 alt_cyls
, alt_heads
, alt_secs
);
537 "between the two geometries to see what is best. Note that the geometry");
541 "must be correct when the table is written or the system may not boot!");
546 /* Show the base and size of the device instead of the whole drive.
547 * This makes sense for subpartitioning primary partitions.
549 if (precise
&& ioctl(device
, DIOCGETP
, &geometry
) >= 0) {
550 table
[0].lowsec
= (unsigned long)(geometry
.base
/ SECTOR_SIZE
);
551 table
[0].size
= (unsigned long)(geometry
.size
/ SECTOR_SIZE
);
559 typedef struct indicators
{ /* Partition type to partition name. */
564 indicators_t ind_table
[]= {
568 { 0x03, "XENIX usr" },
570 { 0x05, "EXTENDED" },
572 { 0x07, "HPFS/NTFS" },
574 { 0x09, "COHERENT" },
579 { 0x0F, "EXTENDED" },
581 { 0x40, "VENIX286" },
582 { 0x42, "W2000 Dyn" },
583 { 0x52, "MICROPORT" },
585 { 0x64, "NOVELL286" },
586 { 0x65, "NOVELL386" },
588 { 0x80, "MINIX-OLD" },
590 { 0x82, "LINUXswap" },
593 { 0x94, "AMOEBAbad" },
596 { 0xB8, "BSDI swap" },
599 { 0xFF, "BADBLOCKS" },
602 char *typ2txt(int ind
)
603 /* Translate a numeric partition indicator for human eyes. */
607 for (pind
= ind_table
; pind
< arraylimit(ind_table
); pind
++) {
608 if (pind
->ind
== ind
) return pind
->name
;
613 int round_sysind(int ind
, int delta
)
614 /* Find the next known partition type starting with ind in direction delta. */
618 ind
= (ind
+ delta
) & 0xFF;
621 for (pind
= arraylimit(ind_table
)-1; pind
->ind
> ind
; pind
--) {}
623 for (pind
= ind_table
; pind
->ind
< ind
; pind
++) {}
628 /* Objects on the screen, either simple pieces of the text or the cylinder
629 * number of the start of partition three.
631 typedef enum objtype
{
632 O_INFO
, O_TEXT
, O_DEV
, O_SUB
,
633 O_TYPTXT
, O_SORT
, O_NUM
, O_TYPHEX
,
634 O_CYL
, O_HEAD
, O_SEC
,
635 O_SCYL
, O_SHEAD
, O_SSEC
, O_LCYL
, O_LHEAD
, O_LSEC
, O_BASE
, O_SIZE
, O_KB
638 #define rjust(type) ((type) >= O_TYPHEX)
639 #define computed(type) ((type) >= O_TYPTXT)
641 typedef struct object
{
643 objtype_t type
; /* Text field, cylinder number, etc. */
644 char flags
; /* Modifiable? */
648 struct part_entry
*entry
; /* What does the object refer to? */
650 char value
[20]; /* Value when printed. */
653 #define OF_MOD 0x01 /* Object value is modifiable. */
654 #define OF_ODD 0x02 /* It has a somewhat odd value. */
655 #define OF_BAD 0x04 /* Its value is no good at all. */
657 /* Events: (Keypress events are the value of the key pressed.) */
658 #define E_ENTER (-1) /* Cursor moves onto object. */
659 #define E_LEAVE (-2) /* Cursor leaves object. */
660 #define E_WRITE (-3) /* Write, but not by typing 'w'. */
662 /* The O_SIZE objects have a dual identity. */
663 enum howend
{ SIZE
, LAST
} howend
= SIZE
;
665 object_t
*world
= nil
;
666 object_t
*curobj
= nil
;
668 object_t
*newobject(objtype_t type
, int flags
, int row
, int col
, int len
)
669 /* Make a new object given a type, flags, position and length on the screen. */
672 object_t
**aop
= &world
;
674 new= alloc(sizeof(*new));
691 unsigned long entry2base(struct part_entry
*pe
)
692 /* Return the base sector of the partition if defined. */
694 return pe
->sysind
== NO_PART
? 0 : pe
->lowsec
;
697 unsigned long entry2last(struct part_entry
*pe
)
699 return pe
->sysind
== NO_PART
? -1 : pe
->lowsec
+ pe
->size
- 1;
702 unsigned long entry2size(struct part_entry
*pe
)
704 return pe
->sysind
== NO_PART
? 0 : pe
->size
;
707 int overlap(unsigned long sec
)
708 /* See if sec is part of another partition. */
710 struct part_entry
*pe
;
712 for (pe
= table
+ 1; pe
<= table
+ NR_PARTITIONS
; pe
++) {
713 if (pe
->sysind
== NO_PART
) continue;
715 if (pe
->lowsec
< sec
&& sec
< pe
->lowsec
+ pe
->size
)
721 int aligned(unsigned long sec
, unsigned unit
)
722 /* True if sec is aligned to unit or if it is no problem if it is unaligned. */
724 return (offset
!= 0 && extbase
== 0) || (sec
% unit
== 0);
727 void print(object_t
*op
)
728 /* Print an object's value if it changed. */
730 struct part_entry
*pe
= op
->entry
;
737 /* Remember the old flags and value. */
739 strcpy(oldvalue
, op
->value
);
741 op
->flags
&= ~(OF_ODD
| OF_BAD
);
746 static struct field
{ int type
; char *name
; } fields
[]= {
747 { O_DEV
, "Select device" },
748 { O_NUM
, "Active flag" },
749 { O_TYPHEX
, "Hex partition type" },
750 { O_TYPTXT
, "Partition type" },
751 { O_SCYL
, "Start cylinder" },
752 { O_SHEAD
, "Start head" },
753 { O_SSEC
, "Start sector" },
754 { O_CYL
, "Number of cylinders" },
755 { O_HEAD
, "Number of heads" },
756 { O_SEC
, "Sectors per track" },
757 { O_LCYL
, "Last cylinder" },
758 { O_LHEAD
, "Last head" },
759 { O_LSEC
, "Last sector" },
760 { O_BASE
, "Base sector" },
761 { O_SIZE
, "Size in sectors" },
762 { O_KB
, "Size in kilobytes" },
765 struct field
*fp
= fields
;
767 while (fp
->type
>= 0 && fp
->type
!= curobj
->type
) fp
++;
768 strcpy(op
->value
, fp
->name
);
772 /* Simple text field. */
773 strcpy(op
->value
, op
->text
);
777 /* Name of currently edited device. */
778 name
= op
->type
== O_DEV
? curdev
->name
:
779 offset
== 0 ? "" : curdev
->subname
;
780 if ((n
= strlen(name
)) < op
->len
) n
= op
->len
;
781 strcpy(op
->value
, name
+ (n
- op
->len
));
782 if (device
< 0 && op
->type
== O_DEV
) op
->flags
|= OF_BAD
;
785 /* Position and active flag. */
786 sprintf(op
->value
, "%d%c", (int) (pe
- table
- 1),
787 pe
->bootind
& ACTIVE_FLAG
? '*' : ' ');
790 /* Position if the driver sorts the table. */
791 sprintf(op
->value
, "%s%d",
792 curdev
->parttype
>= PRIMARY
? "p" :
793 curdev
->parttype
== SUBPART
? "s" : "",
794 (curdev
->parttype
== SUBPART
||
795 curdev
->parttype
== FLOPPY
? pe
- table
796 : sort_index
[pe
- table
]) - 1);
799 /* Hex partition type indicator. */
800 sprintf(op
->value
, "%02X", pe
->sysind
);
803 /* Ascii partition type indicator. */
804 strcpy(op
->value
, typ2txt(pe
->sysind
));
807 /* Partition's start cylinder. */
808 sprintf(op
->value
, "%lu", entry2base(pe
) / secpcyl
);
813 sprintf(op
->value
, "%lu", t
% secpcyl
/ sectors
);
814 if (!aligned(t
, secpcyl
) && t
!= table
[0].lowsec
+ sectors
)
820 sprintf(op
->value
, "%lu", t
% sectors
);
821 if (!aligned(t
, sectors
)) op
->flags
|= OF_ODD
;
824 /* Number of cylinders. */
825 sprintf(op
->value
, "%u", cylinders
);
828 /* Number of heads. */
829 sprintf(op
->value
, "%u", heads
);
832 /* Number of sectors per track. */
833 sprintf(op
->value
, "%u", sectors
);
836 /* Partition's last cylinder. */
838 sprintf(op
->value
, "%lu", t
== -1 ? 0 : t
/ secpcyl
);
841 /* Partition's last head. */
843 sprintf(op
->value
, "%lu", t
== -1 ? 0 : t
% secpcyl
/ sectors
);
844 if (!aligned(t
+ 1, secpcyl
)) op
->flags
|= OF_ODD
;
847 /* Partition's last sector. */
849 if (t
== -1) strcpy(op
->value
, "-1");
850 else sprintf(op
->value
, "%lu", t
% sectors
);
851 if (!aligned(t
+ 1, sectors
)) op
->flags
|= OF_ODD
;
854 /* Partition's base sector. */
855 sprintf(op
->value
, "%lu", entry2base(pe
));
856 if (pe
->sysind
!= NO_PART
&& pe
!= &table
[0]
857 && (pe
->lowsec
<= table
[0].lowsec
|| overlap(pe
->lowsec
)))
861 /* Size of partitition in sectors. */
862 t
= howend
== SIZE
? entry2size(pe
) : entry2last(pe
);
863 sprintf(op
->value
, "%lu", pe
->sysind
== NO_PART
? 0 : t
);
864 if (pe
->sysind
!= NO_PART
&& (pe
->size
== 0
865 || pe
->lowsec
+ pe
->size
> table
[0].lowsec
+ table
[0].size
866 || overlap(pe
->lowsec
+ pe
->size
)))
870 /* Size of partitition in kilobytes. */
871 sprintf(op
->value
, "%lu", entry2size(pe
) / 2);
874 sprintf(op
->value
, "?? %d ??", op
->type
);
877 if (device
< 0 && computed(op
->type
)) strcpy(op
->value
, "?");
879 /* If a value overflows the print field then show a blank
880 * reverse video field.
882 if ((n
= strlen(op
->value
)) > op
->len
) {
887 /* Right or left justified? */
888 if (rjust(op
->type
)) {
889 memmove(op
->value
+ (op
->len
- n
), op
->value
, n
);
890 memset(op
->value
, ' ', op
->len
- n
);
892 memset(op
->value
+ n
, ' ', op
->len
- n
);
894 op
->value
[(int) op
->len
]= 0;
896 if ((op
->flags
& (OF_ODD
| OF_BAD
)) == (oldflags
& (OF_ODD
| OF_BAD
))
897 && strcmp(op
->value
, oldvalue
) == 0) {
898 /* The value did not change. */
902 set_cursor(op
->row
, rjust(op
->type
) ? op
->col
- (op
->len
-1) : op
->col
);
904 if (op
->flags
& OF_BAD
) tputs(t_so
, 1, putchr
);
906 if (op
->flags
& OF_ODD
) tputs(t_md
, 1, putchr
);
908 if (op
->flags
& OF_BAD
) tputs(t_se
, 1, putchr
);
910 if (op
->flags
& OF_ODD
) tputs(t_me
, 1, putchr
);
914 /* Repaint all objects that changed. */
918 for (op
= world
; op
!= nil
; op
= op
->next
) print(op
);
921 int typing
; /* Set if a digit has been typed to set a value. */
922 int magic
; /* Changes when using the magic key. */
924 void event(int ev
, object_t
*op
);
926 void m_redraw(int ev
, object_t
*op
)
927 /* Redraw the screen. */
931 if (ev
!= ctrl('L')) return;
934 for (op2
= world
; op2
!= nil
; op2
= op2
->next
) op2
->value
[0]= 0;
937 void m_toggle(int ev
, object_t
*op
)
938 /* Toggle between the driver and alternate geometry. */
942 if (ev
!= 'X') return;
943 if (alt_cyls
== cylinders
&& alt_heads
== heads
&& alt_secs
== sectors
)
946 t
= cylinders
; cylinders
= alt_cyls
; alt_cyls
= t
;
947 t
= heads
; heads
= alt_heads
; alt_heads
= t
;
948 t
= sectors
; sectors
= alt_secs
; alt_secs
= t
;
953 char size_last
[]= "Size";
955 void m_orientation(int ev
, object_t
*op
)
957 if (ev
!= ' ') return;
962 strcpy(size_last
, "Last");
966 strcpy(size_last
, "Size");
970 void m_move(int ev
, object_t
*op
)
971 /* Move to the nearest modifiably object in the intended direction. Objects
972 * on the same row or column are really near.
975 object_t
*near
, *op2
;
976 unsigned dist
, d2
, dr
, dc
;
978 if (ev
!= 'h' && ev
!= 'j' && ev
!= 'k' && ev
!= 'l' && ev
!= 'H')
982 /* No device open? Then try to read first. */
984 if (device
< 0) return;
990 for (op2
= world
; op2
!= nil
; op2
= op2
->next
) {
991 if (op2
== op
|| !(op2
->flags
& OF_MOD
)) continue;
993 dr
= abs(op2
->row
- op
->row
);
994 dc
= abs(op2
->col
- op
->col
);
996 d2
= 25*dr
*dr
+ dc
*dc
;
997 if (op2
->row
!= op
->row
&& op2
->col
!= op
->col
) d2
+= 1000;
1000 case 'h': /* Left */
1001 if (op2
->col
>= op
->col
) d2
= -1;
1003 case 'j': /* Down */
1004 if (op2
->row
<= op
->row
) d2
= -1;
1007 if (op2
->row
>= op
->row
) d2
= -1;
1009 case 'l': /* Right */
1010 if (op2
->col
<= op
->col
) d2
= -1;
1012 case 'H': /* Home */
1013 if (op2
->type
== O_DEV
) d2
= 0;
1015 if (d2
< dist
) { near
= op2
; dist
= d2
; }
1017 if (near
!= op
) event(E_LEAVE
, op
);
1018 event(E_ENTER
, near
);
1021 void m_updown(int ev
, object_t
*op
)
1022 /* Move a partition table entry up or down. */
1025 struct part_entry tmp
;
1028 if (ev
!= ctrl('K') && ev
!= ctrl('J')) return;
1029 if (op
->entry
== nil
) return;
1031 i
= op
->entry
- table
;
1032 if (ev
== ctrl('K')) {
1036 if (i
>= NR_PARTITIONS
) return;
1040 tmp
= table
[i
]; table
[i
]= table
[j
]; table
[j
]= tmp
;
1041 tmpx
= existing
[i
]; existing
[i
]= existing
[j
]; existing
[j
]= tmpx
;
1044 event(ev
== ctrl('K') ? 'k' : 'j', op
);
1047 void m_enter(int ev
, object_t
*op
)
1048 /* We've moved onto this object. */
1050 if (ev
!= E_ENTER
&& ev
!= ' ' && ev
!= '<' && ev
!= '>' && ev
!= 'X')
1057 void m_leave(int ev
, object_t
*op
)
1058 /* About to leave this object. */
1060 if (ev
!= E_LEAVE
) return;
1063 int within(unsigned *var
, unsigned low
, unsigned value
, unsigned high
)
1064 /* Only set *var to value if it looks reasonable. */
1066 if (low
<= value
&& value
<= high
) {
1073 int lwithin(unsigned long *var
, unsigned long low
, unsigned long value
,
1076 if (low
<= value
&& value
<= high
) {
1083 int nextdevice(object_t
*op
, int delta
)
1084 /* Select the next or previous device from the device list. */
1088 if (offset
!= 0) return 0;
1089 if (dirty
) event(E_WRITE
, op
);
1090 if (dirty
) return 0;
1093 (void) close(device
);
1101 curdev
= curdev
->prev
;
1102 while (delta
< -1 && major(curdev
->rdev
) == major(rdev
)
1103 && curdev
->rdev
< rdev
);
1106 curdev
= curdev
->next
;
1107 while (delta
> 1 && major(curdev
->rdev
) == major(rdev
)
1108 && curdev
->rdev
> rdev
);
1113 void check_ind(struct part_entry
*pe
)
1114 /* If there are no other partitions then make this new one active. */
1116 struct part_entry
*pe2
;
1118 if (pe
->sysind
!= NO_PART
) return;
1120 for (pe2
= table
+ 1; pe2
< table
+ 1 + NR_PARTITIONS
; pe2
++)
1121 if (pe2
->sysind
!= NO_PART
|| pe2
->bootind
& ACTIVE_FLAG
) break;
1123 if (pe2
== table
+ 1 + NR_PARTITIONS
) pe
->bootind
= ACTIVE_FLAG
;
1126 int check_existing(struct part_entry
*pe
)
1127 /* Check and if not ask if an existing partition may be modified. */
1129 static int expert
= 0;
1132 if (expert
|| pe
== nil
|| !existing
[pe
- table
]) return 1;
1135 putstr("Do you wish to modify existing partitions? (y/n) ");
1137 while ((c
= getchar()) != 'y' && c
!= 'n') {}
1140 return (expert
= (c
== 'y'));
1143 void m_modify(int ev
, object_t
*op
)
1144 /* Increment, decrement, set, or toggle the value of an object, using
1145 * arithmetic tricks the author doesn't understand either.
1149 struct part_entry
*pe
= op
->entry
;
1152 unsigned long surplus
;
1153 int radix
= op
->type
== O_TYPHEX
? 0x10 : 10;
1156 if (device
< 0 && op
->type
!= O_DEV
) return;
1160 mul
= radix
; delta
= -1; typing
= 0;
1163 mul
= radix
; delta
= 1; typing
= 0;
1166 if (!typing
) return;
1173 if ('0' <= ev
&& ev
<= '9')
1176 if (radix
== 0x10 && 'a' <= ev
&& ev
<= 'f')
1177 delta
= ev
- 'a' + 10;
1179 if (radix
== 0x10 && 'A' <= ev
&& ev
<= 'F')
1180 delta
= ev
- 'A' + 10;
1184 mul
= typing
? radix
*radix
: 0;
1189 if (!check_existing(pe
)) return;
1193 if (ev
!= '-' && ev
!= '+') return;
1194 if (!nextdevice(op
, delta
)) return;
1197 if (!within(&cylinders
, 1,
1198 cylinders
* mul
/ radix
+ delta
, 1024)) return;
1202 if (!within(&heads
, 1, heads
* mul
/ radix
+ delta
, 255))
1207 if (!within(§ors
, 1, sectors
* mul
/ radix
+ delta
, 63))
1212 if (ev
!= '-' && ev
!= '+') return;
1213 for (op2
= world
; op2
!= nil
; op2
= op2
->next
) {
1214 if (op2
->type
== O_NUM
&& ev
== '+')
1215 op2
->entry
->bootind
= 0;
1217 op
->entry
->bootind
= ev
== '+' ? ACTIVE_FLAG
: 0;
1221 pe
->sysind
= pe
->sysind
* mul
/ radix
+ delta
;
1224 if (ev
!= '-' && ev
!= '+') return;
1226 pe
->sysind
= round_sysind(pe
->sysind
, delta
);
1233 if (op
->type
!= O_SCYL
&& ev
!= '-' && ev
!= '+') return;
1235 if (pe
->sysind
== NO_PART
) memset(pe
, 0, sizeof(*pe
));
1238 if (!lwithin(&t
, 0L,
1239 (t
/ level
* mul
/ radix
+ delta
) * level
+ surplus
,
1241 if (howend
== LAST
|| op
->type
!= O_BASE
)
1242 pe
->size
-= t
- pe
->lowsec
;
1245 if (pe
->sysind
== NO_PART
) pe
->sysind
= MINIX_PART
;
1252 if (op
->type
!= O_LCYL
&& ev
!= '-' && ev
!= '+') return;
1254 if (pe
->sysind
== NO_PART
) memset(pe
, 0, sizeof(*pe
));
1255 t
= pe
->lowsec
+ pe
->size
- 1 + level
;
1256 surplus
= t
% level
- mul
/ radix
* level
;
1257 if (!lwithin(&t
, 0L,
1258 (t
/ level
* mul
/ radix
+ delta
) * level
+ surplus
,
1260 pe
->size
= t
- pe
->lowsec
+ 1;
1262 if (pe
->sysind
== NO_PART
) pe
->sysind
= MINIX_PART
;
1266 if (mul
== 0) pe
->size
= 0; /* new value, no surplus */
1268 if (pe
->sysind
== NO_PART
) {
1269 if (op
->type
== O_KB
|| howend
== SIZE
) {
1270 /* First let loose magic to set the base. */
1277 memset(pe
, 0, sizeof(*pe
));
1279 t
= (op
->type
== O_KB
|| howend
== SIZE
) ? pe
->size
1280 : pe
->lowsec
+ pe
->size
- 1;
1282 if (!lwithin(&t
, 0L,
1283 (t
/ level
* mul
/ radix
+ delta
) * level
+ surplus
,
1285 pe
->size
= (op
->type
== O_KB
|| howend
== SIZE
) ? t
:
1288 if (pe
->sysind
== NO_PART
) pe
->sysind
= MINIX_PART
;
1294 /* The order among the entries may have changed. */
1299 unsigned long spell
[3 + 4 * (1+NR_PARTITIONS
)];
1303 void newspell(unsigned long charm
)
1304 /* Add a new spell, descending order for the base, ascending for the size. */
1308 if (charm
- table
[0].lowsec
> table
[0].size
) return;
1310 for (i
= 0; i
< nspells
; i
++) {
1311 if (charm
== spell
[i
]) return; /* duplicate */
1313 if (touching
== O_BASE
) {
1314 if (charm
== table
[0].lowsec
+ table
[0].size
) return;
1315 if ((spell
[0] - charm
) < (spell
[0] - spell
[i
])) break;
1317 if (charm
== table
[0].lowsec
) return;
1318 if ((charm
- spell
[0]) < (spell
[i
] - spell
[0])) break;
1321 for (j
= ++nspells
; j
> i
; j
--) spell
[j
]= spell
[j
-1];
1325 void m_magic(int ev
, object_t
*op
)
1326 /* Apply magic onto a base or size number. */
1328 struct part_entry
*pe
= op
->entry
, *pe2
;
1329 int rough
= (offset
!= 0 && extbase
== 0);
1331 if (ev
!= 'm' || device
< 0) return;
1334 if (!check_existing(pe
)) return;
1337 /* See what magic we can let loose on this value. */
1340 /* First spell, the current value. */
1343 case O_SHEAD
: /* Start of partition. */
1347 spell
[0]= pe
->lowsec
;
1351 case O_LSEC
: /* End of partition. */
1355 spell
[0]= pe
->lowsec
+ pe
->size
;
1360 if (pe
->sysind
== NO_PART
) {
1361 memset(pe
, 0, sizeof(*pe
));
1363 pe
->sysind
= MINIX_PART
;
1365 if (touching
== O_SIZE
) {
1366 /* First let loose magic on the base. */
1369 for (op2
= world
; op2
!= nil
; op2
= op2
->next
) {
1370 if (op2
->row
== op
->row
&&
1371 op2
->type
== O_BASE
) {
1380 /* Avoid the first sector on the device. */
1381 if (spell
[0] == table
[0].lowsec
) newspell(spell
[0] + 1);
1383 /* Further interesting values are the the bases of other
1384 * partitions or their ends.
1386 for (pe2
= table
; pe2
< table
+ 1 + NR_PARTITIONS
; pe2
++) {
1387 if (pe2
== pe
|| pe2
->sysind
== NO_PART
) continue;
1388 if (pe2
->lowsec
== table
[0].lowsec
)
1389 newspell(table
[0].lowsec
+ 1);
1391 newspell(pe2
->lowsec
);
1392 newspell(pe2
->lowsec
+ pe2
->size
);
1393 if (touching
== O_BASE
&& howend
== SIZE
) {
1394 newspell(pe2
->lowsec
- pe
->size
);
1395 newspell(pe2
->lowsec
+ pe2
->size
- pe
->size
);
1397 if (pe2
->lowsec
% sectors
!= 0) rough
= 1;
1399 /* Present values rounded up to the next cylinder unless
1400 * the table is already a mess. Use "start + 1 track" instead
1401 * of "start + 1 cylinder". Also add the end of the last
1405 unsigned long n
= spell
[0];
1406 if (n
== table
[0].lowsec
) n
++;
1407 n
= (n
+ sectors
- 1) / sectors
* sectors
;
1408 if (n
!= table
[0].lowsec
+ sectors
)
1409 n
= (n
+ secpcyl
- 1) / secpcyl
* secpcyl
;
1411 if (touching
== O_SIZE
)
1412 newspell(table
[0].size
/ secpcyl
* secpcyl
);
1415 /* Magic has been applied, a spell needs to be chosen. */
1417 if (++magic
== nspells
) magic
= 0;
1419 if (touching
== O_BASE
) {
1420 if (howend
== LAST
) pe
->size
-= spell
[magic
] - pe
->lowsec
;
1421 pe
->lowsec
= spell
[magic
];
1423 pe
->size
= spell
[magic
] - pe
->lowsec
;
1425 /* The order among the entries may have changed. */
1430 typedef struct diving
{
1432 struct part_entry old0
;
1434 parttype_t oldparttype
;
1435 unsigned long oldoffset
;
1436 unsigned long oldextbase
;
1439 diving_t
*diving
= nil
;
1441 void m_in(int ev
, object_t
*op
)
1442 /* Go down into a primary or extended partition. */
1445 struct part_entry
*pe
= op
->entry
, ext
;
1448 if (ev
!= '>' || device
< 0 || pe
== nil
|| pe
== &table
[0]
1449 || (!(pe
->sysind
== MINIX_PART
&& offset
== 0)
1450 && !ext_part(pe
->sysind
))
1451 || pe
->size
== 0) return;
1454 if (extbase
!= 0) ext
.size
= extbase
+ extsize
- ext
.lowsec
;
1456 if (dirty
) event(E_WRITE
, op
);
1458 if (device
>= 0) { close(device
); device
= -1; }
1460 newdiv
= alloc(sizeof(*newdiv
));
1461 newdiv
->old0
= table
[0];
1462 newdiv
->oldsubname
= curdev
->subname
;
1463 newdiv
->oldparttype
= curdev
->parttype
;
1464 newdiv
->oldoffset
= offset
;
1465 newdiv
->oldextbase
= extbase
;
1471 n
= strlen(diving
->oldsubname
);
1472 curdev
->subname
= alloc((n
+ 3) * sizeof(curdev
->subname
[0]));
1473 strcpy(curdev
->subname
, diving
->oldsubname
);
1474 curdev
->subname
[n
++]= ':';
1475 curdev
->subname
[n
++]= '0' + (pe
- table
- 1);
1476 curdev
->subname
[n
]= 0;
1478 curdev
->parttype
= curdev
->parttype
== PRIMARY
? SUBPART
: DUNNO
;
1480 if (ext_part(ext
.sysind
) && extbase
== 0) {
1481 extbase
= ext
.lowsec
;
1483 curdev
->parttype
= DUNNO
;
1490 void m_out(int ev
, object_t
*op
)
1491 /* Go up from an extended or subpartition table to its enclosing. */
1495 if (ev
!= '<' || diving
== nil
) return;
1497 if (dirty
) event(E_WRITE
, op
);
1499 if (device
>= 0) { close(device
); device
= -1; }
1504 table
[0]= olddiv
->old0
;
1506 free(curdev
->subname
);
1507 curdev
->subname
= olddiv
->oldsubname
;
1509 curdev
->parttype
= olddiv
->oldparttype
;
1510 offset
= olddiv
->oldoffset
;
1511 extbase
= olddiv
->oldextbase
;
1516 if (diving
== nil
) submerged
= 0; /* We surfaced. */
1519 void installboot(unsigned char *bootblock
, char *masterboot
)
1520 /* Install code from a master bootstrap into a boot block. */
1523 unsigned char buf
[SECTOR_SIZE
];
1527 if ((mfp
= fopen(masterboot
, "r")) == nil
) {
1528 err
= strerror(errno
);
1532 n
= fread(buf
, sizeof(char), SECTOR_SIZE
, mfp
);
1534 err
= strerror(errno
);
1539 err
= "Is probably not a boot sector, too small";
1543 else if (n
< SECTOR_SIZE
&& n
> PART_TABLE_OFF
) {
1544 /* if only code, it cannot override partition table */
1545 err
= "Does not fit in a boot sector";
1549 else if (n
== SECTOR_SIZE
) {
1550 if (buf
[510] != 0x55 || buf
[511] != 0xaa) {
1551 err
= "Is not a boot sector (bad magic)";
1558 if (n
> PART_TABLE_OFF
) {
1559 err
= "Does not fit in a boot sector";
1564 memcpy(bootblock
, buf
, n
);
1567 /* Bootstrap installed. */
1572 printf("%s: %s", masterboot
, err
);
1576 ssize_t
boot_readwrite(int rw
)
1577 /* Read (0) or write (1) the boot sector. */
1581 if (lseek(device
, (off_t
)offset
* SECTOR_SIZE
, SEEK_SET
) < 0)
1585 case 0: r
= read(device
, bootblock
, SECTOR_SIZE
); break;
1586 case 1: r
= write(device
, bootblock
, SECTOR_SIZE
); break;
1592 void m_read(int ev
, object_t
*op
)
1593 /* Read the partition table from the current device. */
1596 struct part_entry
*pe
;
1598 if (ev
!= 'r' || device
>= 0) return;
1600 /* Open() may cause kernel messages: */
1604 if (((device
= open(curdev
->name
, mode
= O_RDWR
, 0666)) < 0
1606 || (device
= open(curdev
->name
, mode
= O_RDONLY
)) < 0))
1609 printf("%s: %s", curdev
->name
, strerror(errno
));
1611 if (device
>= 0) { close(device
); device
= -1; }
1615 /* Assume up to five lines of kernel messages. */
1619 if (mode
== O_RDONLY
) {
1621 printf("%s: Readonly", curdev
->name
);
1624 memset(bootblock
, 0, sizeof(bootblock
));
1626 n
= boot_readwrite(0);
1628 if (n
<= 0) stat_start(1);
1630 printf("%s: %s", curdev
->name
, strerror(errno
));
1634 if (n
< SECTOR_SIZE
) printf("%s: Unexpected EOF", curdev
->subname
);
1635 if (n
<= 0) stat_end(5);
1637 if (n
< SECTOR_SIZE
) n
= SECTOR_SIZE
;
1639 memcpy(table
+1, bootblock
+PART_TABLE_OFF
,
1640 NR_PARTITIONS
* sizeof(table
[1]));
1641 for (i
= 1; i
<= NR_PARTITIONS
; i
++) {
1642 if ((table
[i
].bootind
& ~ACTIVE_FLAG
) != 0) break;
1644 if (i
<= NR_PARTITIONS
|| bootblock
[510] != 0x55
1645 || bootblock
[511] != 0xAA) {
1646 /* Invalid boot block, install bootstrap, wipe partition table.
1648 memset(bootblock
, 0, sizeof(bootblock
));
1649 installboot(bootblock
, MASTERBOOT
);
1650 memset(table
+1, 0, NR_PARTITIONS
* sizeof(table
[1]));
1652 printf("%s: Invalid partition table (reset)", curdev
->subname
);
1656 /* Fix an extended partition table up to something mere mortals can
1657 * understand. Record already defined partitions.
1659 for (i
= 1; i
<= NR_PARTITIONS
; i
++) {
1661 if (extbase
!= 0 && pe
->sysind
!= NO_PART
)
1662 pe
->lowsec
+= ext_part(pe
->sysind
) ? extbase
: offset
;
1663 existing
[i
]= pe
->sysind
!= NO_PART
;
1668 /* Warn about grave dangers ahead. */
1671 printf("Warning: You are in an extended partition.");
1676 void m_write(int ev
, object_t
*op
)
1677 /* Write the partition table back if modified. */
1680 struct part_entry new_table
[NR_PARTITIONS
], *pe
;
1682 if (ev
!= 'w' && ev
!= E_WRITE
) return;
1683 if (device
< 0) { dirty
= 0; return; }
1687 printf("%s is not changed, or has already been written",
1694 if (bootblock
[510] != 0x55 || bootblock
[511] != 0xAA) {
1695 /* Invalid boot block, warn user. */
1697 printf("Warning: About to write a new table on %s",
1702 /* Will this stop the luser? Probably not... */
1704 printf("You have changed an extended partition. Bad Idea.");
1708 putstr("Save partition table? (y/n) ");
1711 while ((c
= getchar()) != 'y' && c
!= 'n' && c
!= ctrl('?')) {}
1713 if (c
== ctrl('?')) putstr("DEL"); else putchr(c
);
1715 if (c
== 'n' && ev
== E_WRITE
) dirty
= 0;
1716 if (c
!= 'y') return;
1718 memcpy(new_table
, table
+1, NR_PARTITIONS
* sizeof(table
[1]));
1719 for (pe
= new_table
; pe
< new_table
+ NR_PARTITIONS
; pe
++) {
1720 if (pe
->sysind
== NO_PART
) {
1721 memset(pe
, 0, sizeof(*pe
));
1723 abs2dos(&pe
->start_head
, pe
->lowsec
);
1724 abs2dos(&pe
->last_head
, pe
->lowsec
+ pe
->size
- 1);
1726 /* Fear and loathing time: */
1728 pe
->lowsec
-= ext_part(pe
->sysind
)
1732 memcpy(bootblock
+PART_TABLE_OFF
, new_table
, sizeof(new_table
));
1733 bootblock
[510]= 0x55;
1734 bootblock
[511]= 0xAA;
1736 if (boot_readwrite(1) < 0) {
1738 printf("%s: %s", curdev
->name
, strerror(errno
));
1745 void m_shell(int ev
, object_t
*op
)
1746 /* Shell escape, to do calculations for instance. */
1749 void (*sigint
)(int), (*sigquit
)(int), (*sigterm
)(int);
1751 if (ev
!= 's') return;
1756 switch (pid
= fork()) {
1759 printf("can't fork: %s\n", strerror(errno
));
1763 if (device
>= 0) (void) close(device
);
1764 execl("/bin/sh", "sh", (char *) nil
);
1767 printf("/bin/sh: %s\n", strerror(errno
));
1771 sigint
= signal(SIGINT
, SIG_IGN
);
1772 sigquit
= signal(SIGQUIT
, SIG_IGN
);
1773 sigterm
= signal(SIGTERM
, SIG_IGN
);
1774 while (pid
>= 0 && (r
= wait(&status
)) >= 0 && r
!= pid
) {}
1775 (void) signal(SIGINT
, sigint
);
1776 (void) signal(SIGQUIT
, sigquit
);
1777 (void) signal(SIGTERM
, sigterm
);
1782 if (WIFEXITED(status
) && WEXITSTATUS(status
) == 127)
1783 stat_start(0); /* Match the stat_start in the child. */
1785 event(ctrl('L'), op
);
1788 void m_dump(int ev
, object_t
*op
)
1789 /* Raw dump of the partition table. */
1791 struct part_entry table
[NR_PARTITIONS
], *pe
;
1795 if (ev
!= 'p' || device
< 0) return;
1797 memcpy(table
, bootblock
+PART_TABLE_OFF
,
1798 NR_PARTITIONS
* sizeof(table
[0]));
1799 for (i
= 0; i
< NR_PARTITIONS
; i
++) {
1802 dos2chs(&pe
->start_head
, chs
);
1803 printf("%2d%c %02X%15d%5d%4d",
1805 pe
->bootind
& ACTIVE_FLAG
? '*' : ' ',
1807 chs
[0], chs
[1], chs
[2]);
1808 dos2chs(&pe
->last_head
, chs
);
1809 printf("%6d%5d%4d%10u%10u%9u",
1810 chs
[0], chs
[1], chs
[2],
1812 howend
== SIZE
? pe
->size
: pe
->size
+ pe
->lowsec
- 1,
1817 printf("(Raw dump of the original %.40s table)",
1824 void m_quit(int ev
, object_t
*op
)
1825 /* Write the partition table if modified and exit. */
1827 if (ev
!= 'q' && ev
!= 'x') return;
1831 if (dirty
) event(E_WRITE
, op
);
1832 if (dirty
) quitting
= 0;
1835 void m_help(int ev
, object_t
*op
)
1836 /* For people without a clue; let's hope they can find the '?' key. */
1838 static struct help
{
1842 { "? !", "This help / more advice!" },
1843 { "+ - (= _ PgUp PgDn)","Select/increment/decrement/make active" },
1844 { "0-9 (a-f)", "Enter value" },
1845 { "hjkl (arrow keys)", "Move around" },
1846 { "CTRL-K CTRL-J", "Move entry up/down" },
1847 { "CTRL-L", "Redraw screen" },
1848 { ">", "Start a subpartition table" },
1849 { "<", "Back to the primary partition table" },
1850 { "m", "Cycle through magic values" },
1851 { "spacebar", "Show \"Size\" or \"Last\"" },
1852 { "r w", "Read/write partition table" },
1853 { "p s q x", "Raw dump / Shell escape / Quit / Exit" },
1854 { "y n DEL", "Answer \"yes\", \"no\", \"cancel\"" },
1856 static char *advice
[] = {
1857 "* Choose a disk with '+' and '-', then hit 'r'.",
1858 "* To change any value: Move to it and use '+', '-' or type the desired value.",
1859 "* To make a new partition: Move over to the Size or Kb field of an unused",
1860 " partition and type the size. Hit the 'm' key to pad the partition out to",
1861 " a cylinder boundary. Hit 'm' again to pad it out to the end of the disk.",
1862 " You can hit 'm' more than once on a base or size field to see several",
1863 " interesting values go by. Note: Other Operating Systems can be picky about",
1864 " partitions that are not padded to cylinder boundaries. Look for highlighted",
1865 " head or sector numbers.",
1866 "* To reuse a partition: Change the type to MINIX.",
1867 "* To delete a partition: Type a zero in the hex Type field.",
1868 "* To make a partition active: Type '+' in the Num field.",
1869 "* To study the list of keys: Type '?'.",
1875 for (hp
= help
; hp
< arraylimit(help
); hp
++) {
1877 printf("%-25s - %s", hp
->keys
, hp
->what
);
1881 putstr("Things like ");
1882 putstr(t_so
); putstr("this"); putstr(t_se
);
1883 putstr(" must be checked, but ");
1884 putstr(t_md
); putstr("this"); putstr(t_me
);
1885 putstr(" is not really a problem");
1891 for (ap
= advice
; ap
< arraylimit(advice
); ap
++) {
1899 void event(int ev
, object_t
*op
)
1900 /* Simply call all modifiers for an event, each one knows when to act. */
1905 m_orientation(ev
, op
);
1922 /* Get a single keypress. Translate compound keypresses (arrow keys) to
1923 * their simpler equivalents.
1930 set_cursor(curobj
->row
, curobj
->col
);
1934 if (read(0, &ch
, sizeof(ch
)) < 0) fatal("stdin");
1935 c
= (unsigned char) ch
;
1939 case ctrl('['): esc
= 1; break;
1940 case '_': c
= '-'; break;
1941 case '=': c
= '+'; break;
1945 esc
= c
== '[' ? 2 : 0;
1949 case 'D': c
= 'h'; break;
1950 case 'B': c
= 'j'; break;
1951 case 'A': c
= 'k'; break;
1952 case 'C': c
= 'l'; break;
1953 case 'H': c
= 'H'; break;
1955 case 'S': c
= '-'; break;
1957 case 'T': c
= '+'; break;
1966 case ctrl('B'): c
= 'h'; break;
1967 case ctrl('N'): c
= 'j'; break;
1968 case ctrl('P'): c
= 'k'; break;
1969 case ctrl('F'): c
= 'l'; break;
1976 /* Get keypress, handle event, display results, reset screen, ad infinitum. */
1991 int main(int argc
, char **argv
)
1995 struct part_entry
*pe
;
1997 /* Define a few objects to show on the screen. First text: */
1998 op
= newobject(O_INFO
, 0, 0, 2, 19);
1999 op
= newobject(O_TEXT
, 0, 0, 22, 13); op
->text
= "----first----";
2000 op
= newobject(O_TEXT
, 0, 0, 37, 13); op
->text
= "--geom/last--";
2001 op
= newobject(O_TEXT
, 0, 0, 52, 18); op
->text
= "------sectors-----";
2002 op
= newobject(O_TEXT
, 0, 1, 4, 6); op
->text
= "Device";
2003 op
= newobject(O_TEXT
, 0, 1, 23, 12); op
->text
= "Cyl Head Sec";
2004 op
= newobject(O_TEXT
, 0, 1, 38, 12); op
->text
= "Cyl Head Sec";
2005 op
= newobject(O_TEXT
, 0, 1, 56, 4); op
->text
= "Base";
2006 op
= newobject(O_TEXT
, 0, 1, 66, 4); op
->text
= size_last
;
2007 op
= newobject(O_TEXT
, 0, 1, 78, 2); op
->text
= "Kb";
2008 op
= newobject(O_TEXT
, 0, 4, 0, 15); op
->text
= "Num Sort Type";
2010 /* The device is the current object: */
2011 curobj
= newobject(O_DEV
, OF_MOD
, 2, 4, 15);
2012 op
= newobject(O_SUB
, 0, 3, 4, 15);
2015 op
= newobject(O_CYL
, OF_MOD
, 2, 40, 5); op
->entry
= &table
[0];
2016 op
= newobject(O_HEAD
, OF_MOD
, 2, 45, 3); op
->entry
= &table
[0];
2017 op
= newobject(O_SEC
, OF_MOD
, 2, 49, 2); op
->entry
= &table
[0];
2019 /* Objects for the device: */
2020 op
= newobject(O_SCYL
, 0, 3, 25, 5); op
->entry
= &table
[0];
2021 op
= newobject(O_SHEAD
, 0, 3, 30, 3); op
->entry
= &table
[0];
2022 op
= newobject(O_SSEC
, 0, 3, 34, 2); op
->entry
= &table
[0];
2023 op
= newobject(O_LCYL
, 0, 3, 40, 5); op
->entry
= &table
[0];
2024 op
= newobject(O_LHEAD
, 0, 3, 45, 3); op
->entry
= &table
[0];
2025 op
= newobject(O_LSEC
, 0, 3, 49, 2); op
->entry
= &table
[0];
2026 op
= newobject(O_BASE
, 0, 3, 59, 9); op
->entry
= &table
[0];
2027 op
= newobject(O_SIZE
, 0, 3, 69, 9); op
->entry
= &table
[0];
2028 op
= newobject(O_KB
, 0, 3, 79, 9); op
->entry
= &table
[0];
2030 /* Objects for each partition: */
2031 for (r
= 5, pe
= table
+1; pe
<= table
+NR_PARTITIONS
; r
++, pe
++) {
2032 op
= newobject(O_NUM
, OF_MOD
, r
, 1, 2); op
->entry
= pe
;
2033 op
= newobject(O_SORT
, 0, r
, 5, 2); op
->entry
= pe
;
2034 op
= newobject(O_TYPHEX
, OF_MOD
, r
, 10, 2); op
->entry
= pe
;
2035 op
= newobject(O_TYPTXT
, OF_MOD
, r
, 12, 9); op
->entry
= pe
;
2036 op
= newobject(O_SCYL
, OF_MOD
, r
, 25, 5); op
->entry
= pe
;
2037 op
= newobject(O_SHEAD
, OF_MOD
, r
, 30, 3); op
->entry
= pe
;
2038 op
= newobject(O_SSEC
, OF_MOD
, r
, 34, 2); op
->entry
= pe
;
2039 op
= newobject(O_LCYL
, OF_MOD
, r
, 40, 5); op
->entry
= pe
;
2040 op
= newobject(O_LHEAD
, OF_MOD
, r
, 45, 3); op
->entry
= pe
;
2041 op
= newobject(O_LSEC
, OF_MOD
, r
, 49, 2); op
->entry
= pe
;
2042 op
= newobject(O_BASE
, OF_MOD
, r
, 59, 9); op
->entry
= pe
;
2043 op
= newobject(O_SIZE
, OF_MOD
, r
, 69, 9); op
->entry
= pe
;
2044 op
= newobject(O_KB
, OF_MOD
, r
, 79, 9); op
->entry
= pe
;
2047 for (i
= 1; i
< argc
; i
++) newdevice(argv
[i
], 0);
2049 if (firstdev
== nil
) {
2056 if (firstdev
!= nil
) {