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 <minix/u64.h>
26 #include <machine/partition.h>
29 /* True if a partition is an extended partition. */
30 #define ext_part(s) ((s) == 0x05 || (s) == 0x0F)
32 /* Minix master bootstrap code. */
33 static char MASTERBOOT
[] = "/usr/mdec/mbr";
36 ----first---- --geom/last-- ------sectors-----
37 Device Cyl Head Sec Cyl Head Sec Base Size Kb
39 /dev/c0d0:2 0 0 2 976 4 16 2 83043 41521
41 0* p0 81 MINIX 0 0 3 33 4 9 3 2880 1440
42 1 p1 81 MINIX 33 4 10 178 2 2 2883 12284 6142
43 2 p2 81 MINIX 178 2 3 976 4 16 15167 67878 33939
44 3 p3 00 None 0 0 0 0 0 -1 0 0 0
47 #define MAXSIZE 999999999L /* Will 1T be enough this year? */
48 #define SECTOR_SIZE 512
49 #define DEV_FD0 0x200 /* Device number of /dev/fd0 */
50 #define DEV_C0D0 0x300 /* Device number of /dev/c0d0 */
52 #define arraysize(a) (sizeof(a) / sizeof((a)[0]))
53 #define arraylimit(a) ((a) + arraysize(a))
55 void report(const char *label
)
57 fprintf(stderr
, "part: %s: %s\n", label
, strerror(errno
));
60 void fatal(const char *label
)
66 struct termios termios
;
68 void save_ttyflags(void)
69 /* Save tty attributes for later restoration. */
71 if (tcgetattr(0, &termios
) < 0) fatal("");
74 void restore_ttyflags(void)
75 /* Reset the tty flags to how we got 'em. */
77 if (tcsetattr(0, TCSANOW
, &termios
) < 0) fatal("");
81 /* Set the terminal to raw mode, no signals, no echoing. */
83 struct termios rawterm
;
86 rawterm
.c_lflag
&= ~(ICANON
|ISIG
|ECHO
);
87 rawterm
.c_iflag
&= ~(ICRNL
);
88 if (tcsetattr(0, TCSANOW
, &rawterm
) < 0) fatal("");
91 #define ctrl(c) ((c) == '?' ? '\177' : ((c) & '\37'))
93 char t_cd
[16], t_cm
[32], t_so
[16], t_se
[16], t_md
[16], t_me
[16];
98 /* Get terminal capabilities and set the tty to "editor" mode. */
101 static char termbuf
[1024];
104 if ((term
= getenv("TERM")) == nil
|| tgetent(termbuf
, term
) != 1) {
105 fprintf(stderr
, "part: Can't get terminal capabilities\n");
108 if (tgetstr("cd", (tp
= t_cd
, &tp
)) == nil
109 || tgetstr("cm", (tp
= t_cm
, &tp
)) == nil
) {
110 fprintf(stderr
, "part: This terminal is too dumb\n");
115 (void) tgetstr("so", (tp
= t_so
, &tp
));
116 (void) tgetstr("se", (tp
= t_se
, &tp
));
117 (void) tgetstr("md", (tp
= t_md
, &tp
));
118 (void) tgetstr("me", (tp
= t_me
, &tp
));
133 while ((c
= *s
++) != 0) putchr(c
);
136 void set_cursor(int row
, int col
)
138 tputs(tgoto(t_cm
, col
, row
), 1, putchr
);
141 int statusrow
= STATUSROW
;
145 void stat_start(int serious
)
146 /* Prepare for printing on a fresh status line, possibly highlighted. */
148 set_cursor(statusrow
++, 0);
149 tputs(t_cd
, 1, putchr
);
150 if (serious
) tputs(t_so
, 1, putchr
);
153 void stat_end(int ktl
)
154 /* Closing bracket for stat_start. Sets "keystrokes to live" of message. */
156 tputs(t_se
, 1, putchr
);
161 void stat_reset(void)
162 /* Reset the statusline pointer and clear old messages if expired. */
164 if (stat_ktl
> 0 && --stat_ktl
== 0) {
165 statusrow
= STATUSROW
;
168 if (need_help
&& statusrow
< (24-2)) {
169 if (statusrow
> STATUSROW
) stat_start(0);
172 "Type '+' or '-' to change, 'r' to read, '?' for more help, 'q' to exit");
174 statusrow
= STATUSROW
;
178 void clear_screen(void)
181 tputs(t_cd
, 1, putchr
);
187 /* Reset the tty to cooked mode. */
190 set_cursor(statusrow
, 0);
191 tputs(t_cd
, 1, putchr
);
194 void *alloc(size_t n
)
198 if ((m
= malloc(n
)) == nil
) { reset_tty(); fatal(""); }
203 typedef enum parttype
{ DUNNO
, SUBPART
, PRIMARY
, FLOPPY
} parttype_t
;
205 typedef struct device
{
206 struct device
*next
, *prev
; /* Circular dequeue. */
207 dev_t rdev
; /* Device number (sorting only). */
208 char *name
; /* E.g. /dev/c0d0 */
209 char *subname
; /* E.g. /dev/c0d0:2 */
213 device_t
*firstdev
= nil
, *curdev
;
215 void newdevice(char *name
, int scanning
)
216 /* Add a device to the device list. If scanning is set then we are reading
217 * /dev, so insert the device in device number order and make /dev/c0d0 current.
220 device_t
*new, *nextdev
, *prevdev
;
225 if (stat(name
, &st
) < 0 || !S_ISBLK(st
.st_mode
)) return;
227 switch (major(st
.st_rdev
)) {
230 if (minor(st
.st_rdev
) >= 4) return;
237 /* Disk controller */
238 if (minor(st
.st_rdev
) >= 0x80
239 || minor(st
.st_rdev
) % 5 != 0) return;
244 /* Interesting device found. */
246 (void) stat(name
, &st
);
249 new= alloc(sizeof(*new));
250 new->rdev
= st
.st_rdev
;
251 new->name
= alloc((strlen(name
) + 1) * sizeof(new->name
[0]));
252 strcpy(new->name
, name
);
253 new->subname
= new->name
;
254 new->parttype
= DUNNO
;
255 if (major(st
.st_rdev
) == major(DEV_FD0
) && minor(st
.st_rdev
) < 112) {
256 new->parttype
= FLOPPY
;
258 if (st
.st_rdev
>= DEV_C0D0
&& minor(st
.st_rdev
) < 128
259 && minor(st
.st_rdev
) % 5 == 0) {
260 new->parttype
= PRIMARY
;
263 if (firstdev
== nil
) {
265 new->next
= new->prev
= new;
270 while (new->rdev
>= nextdev
->rdev
271 && (nextdev
= nextdev
->next
) != firstdev
) {}
272 prevdev
= nextdev
->prev
;
278 if (new->rdev
< firstdev
->rdev
) firstdev
= new;
279 if (new->rdev
== DEV_C0D0
) curdev
= new;
280 if (curdev
->rdev
!= DEV_C0D0
) curdev
= firstdev
;
283 void getdevices(void)
284 /* Get all block devices from /dev that look interesting. */
288 char name
[5 + NAME_MAX
+ 1];
290 if ((d
= opendir("/dev")) == nil
) fatal("/dev");
292 while ((e
= readdir(d
)) != nil
) {
293 strcpy(name
, "/dev/");
294 strcpy(name
+ 5, e
->d_name
);
301 unsigned char bootblock
[SECTOR_SIZE
];
302 struct part_entry table
[1 + NR_PARTITIONS
];
303 int existing
[1 + NR_PARTITIONS
];
304 unsigned long offset
= 0, extbase
= 0, extsize
;
306 char sort_index
[1 + NR_PARTITIONS
];
307 unsigned cylinders
= 1, heads
= 1, sectors
= 1, secpcyl
= 1;
308 unsigned alt_cyls
= 1, alt_heads
= 1, alt_secs
= 1;
312 unsigned long sortbase(struct part_entry
*pe
)
314 return pe
->sysind
== NO_PART
? -1 : pe
->lowsec
;
318 /* Let the sort_index array show the order partitions are sorted in. */
321 int idx
[1 + NR_PARTITIONS
];
323 for (i
= 1; i
<= NR_PARTITIONS
; i
++) idx
[i
]= i
;
325 for (i
= 1; i
<= NR_PARTITIONS
; i
++) {
326 for (j
= 1; j
<= NR_PARTITIONS
-1; j
++) {
327 int sj
= idx
[j
], sj1
= idx
[j
+1];
329 if (sortbase(&table
[sj
]) > sortbase(&table
[sj1
])) {
335 for (i
= 1; i
<= NR_PARTITIONS
; i
++) sort_index
[idx
[i
]]= i
;
338 void dos2chs(unsigned char *dos
, unsigned *chs
)
339 /* Extract cylinder, head and sector from the three bytes DOS uses to address
340 * a sector. Note that bits 8 & 9 of the cylinder number come from bit 6 & 7
341 * of the sector byte. The sector number is rebased to count from 0.
344 chs
[0]= ((dos
[1] & 0xC0) << 2) | dos
[2];
346 chs
[2]= (dos
[1] & 0x3F) - 1;
349 void abs2dos(unsigned char *dos
, unsigned long pos
)
350 /* Translate a sector offset to three DOS bytes. */
355 h
= (pos
% secpcyl
) / sectors
;
356 s
= pos
% sectors
+ 1;
359 dos
[1]= s
| ((c
>> 2) & 0xC0);
363 void recompute0(void)
364 /* Recompute the partition size for the device after a geometry change. */
367 cylinders
= heads
= sectors
= 1;
368 memset(table
, 0, sizeof(table
));
370 if (!precise
&& offset
== 0) {
372 table
[0].size
= (unsigned long) cylinders
* heads
* sectors
;
374 table
[0].sysind
= device
< 0 ? NO_PART
: MINIX_PART
;
375 secpcyl
= heads
* sectors
;
378 void guess_geometry(void)
379 /* With a bit of work one can deduce the disk geometry from the partition
380 * table. This may be necessary if the driver gets it wrong. (If partition
381 * tables didn't have C/H/S numbers we would not care at all...)
385 struct part_entry
*pe
;
389 unsigned char HS
[256][8]; /* Bit map off all possible H/S */
391 alt_cyls
= alt_heads
= alt_secs
= 0;
393 /* Initially all possible H/S combinations are possible. HS[h][0]
394 * bit 0 is used to rule out a head value.
396 for (h
= 1; h
<= 255; h
++) {
397 for (s
= 0; s
< 8; s
++) HS
[h
][s
]= 0xFF;
400 for (i
= 0; i
< 2*NR_PARTITIONS
; i
++) {
401 pe
= &(table
+1)[i
>> 1];
402 if (pe
->sysind
== NO_PART
) continue;
404 /* Get the end or start sector numbers (in that order). */
406 dos2chs(&pe
->last_head
, chs
);
407 sec
= pe
->lowsec
+ pe
->size
- 1;
409 dos2chs(&pe
->start_head
, chs
);
413 if (chs
[0] >= alt_cyls
) alt_cyls
= chs
[0]+1;
415 /* Which H/S combinations can be ruled out? */
416 for (h
= 1; h
<= 255; h
++) {
417 if (HS
[h
][0] == 0) continue;
419 for (s
= 1; s
<= 63; s
++) {
420 if ((chs
[0] * h
+ chs
[1]) * s
+ chs
[2] != sec
) {
421 HS
[h
][s
/8] &= ~(1 << (s
%8));
423 if (HS
[h
][s
/8] & (1 << (s
%8))) n
++;
425 if (n
== 0) HS
[h
][0]= 0;
429 /* See if only one remains. */
431 for (h
= 1; h
<= 255; h
++) {
432 if (HS
[h
][0] == 0) continue;
433 for (s
= 1; s
<= 63; s
++) {
434 if (HS
[h
][s
/8] & (1 << (s
%8))) {
442 /* Forget it if more than one choice... */
443 if (i
> 1) alt_cyls
= alt_heads
= alt_secs
= 0;
447 /* Find out the geometry of the device by querying the driver, or by looking
448 * at the partition table. These numbers are crosschecked to make sure that
449 * the geometry is correct. Master bootstraps other than the Minix one use
450 * the CHS numbers in the partition table to load the bootstrap of the active
456 struct partition geometry
;
459 /* Geometry already known. */
466 if (device
< 0) return;
468 /* Try to guess the geometry from the partition table. */
471 /* Try to get the geometry from the driver. */
472 (void) fstat(device
, &dst
);
474 if (S_ISBLK(dst
.st_mode
) || S_ISCHR(dst
.st_mode
)) {
475 /* Try to get the drive's geometry from the driver. */
477 if (ioctl(device
, DIOCGETP
, &geometry
) < 0)
480 table
[0].lowsec
= div64u(geometry
.base
, SECTOR_SIZE
);
481 table
[0].size
= div64u(geometry
.size
, SECTOR_SIZE
);
482 cylinders
= geometry
.cylinders
;
483 heads
= geometry
.heads
;
484 sectors
= geometry
.sectors
;
492 /* Getting the geometry from the driver failed, so use the
493 * alternate geometry.
495 if (alt_heads
== 0) {
496 alt_cyls
= table
[0].size
/ (64 * 32);
506 printf("Failure to get the geometry of %s: %s", curdev
->name
,
507 errno
== ENOTTY
? "No driver support" : strerror(err
));
510 printf("The geometry has been guessed as %ux%ux%u",
511 cylinders
, heads
, sectors
);
514 if (alt_heads
== 0) {
520 if (heads
!= alt_heads
|| sectors
!= alt_secs
) {
526 "The %ux%ux%u geometry obtained from the device driver does not match",
527 cylinders
, heads
, sectors
);
531 "the %ux%ux%u geometry implied by the partition table. Hit 'X' to switch",
532 alt_cyls
, alt_heads
, alt_secs
);
536 "between the two geometries to see what is best. Note that the geometry");
540 "must be correct when the table is written or the system may not boot!");
545 /* Show the base and size of the device instead of the whole drive.
546 * This makes sense for subpartitioning primary partitions.
548 if (precise
&& ioctl(device
, DIOCGETP
, &geometry
) >= 0) {
549 table
[0].lowsec
= div64u(geometry
.base
, SECTOR_SIZE
);
550 table
[0].size
= div64u(geometry
.size
, SECTOR_SIZE
);
558 typedef struct indicators
{ /* Partition type to partition name. */
563 indicators_t ind_table
[]= {
567 { 0x03, "XENIX usr" },
569 { 0x05, "EXTENDED" },
571 { 0x07, "HPFS/NTFS" },
573 { 0x09, "COHERENT" },
578 { 0x0F, "EXTENDED" },
580 { 0x40, "VENIX286" },
581 { 0x42, "W2000 Dyn" },
582 { 0x52, "MICROPORT" },
584 { 0x64, "NOVELL286" },
585 { 0x65, "NOVELL386" },
587 { 0x80, "MINIX-OLD" },
589 { 0x82, "LINUXswap" },
592 { 0x94, "AMOEBAbad" },
595 { 0xB8, "BSDI swap" },
598 { 0xFF, "BADBLOCKS" },
601 char *typ2txt(int ind
)
602 /* Translate a numeric partition indicator for human eyes. */
606 for (pind
= ind_table
; pind
< arraylimit(ind_table
); pind
++) {
607 if (pind
->ind
== ind
) return pind
->name
;
612 int round_sysind(int ind
, int delta
)
613 /* Find the next known partition type starting with ind in direction delta. */
617 ind
= (ind
+ delta
) & 0xFF;
620 for (pind
= arraylimit(ind_table
)-1; pind
->ind
> ind
; pind
--) {}
622 for (pind
= ind_table
; pind
->ind
< ind
; pind
++) {}
627 /* Objects on the screen, either simple pieces of the text or the cylinder
628 * number of the start of partition three.
630 typedef enum objtype
{
631 O_INFO
, O_TEXT
, O_DEV
, O_SUB
,
632 O_TYPTXT
, O_SORT
, O_NUM
, O_TYPHEX
,
633 O_CYL
, O_HEAD
, O_SEC
,
634 O_SCYL
, O_SHEAD
, O_SSEC
, O_LCYL
, O_LHEAD
, O_LSEC
, O_BASE
, O_SIZE
, O_KB
637 #define rjust(type) ((type) >= O_TYPHEX)
638 #define computed(type) ((type) >= O_TYPTXT)
640 typedef struct object
{
642 objtype_t type
; /* Text field, cylinder number, etc. */
643 char flags
; /* Modifiable? */
647 struct part_entry
*entry
; /* What does the object refer to? */
649 char value
[20]; /* Value when printed. */
652 #define OF_MOD 0x01 /* Object value is modifiable. */
653 #define OF_ODD 0x02 /* It has a somewhat odd value. */
654 #define OF_BAD 0x04 /* Its value is no good at all. */
656 /* Events: (Keypress events are the value of the key pressed.) */
657 #define E_ENTER (-1) /* Cursor moves onto object. */
658 #define E_LEAVE (-2) /* Cursor leaves object. */
659 #define E_WRITE (-3) /* Write, but not by typing 'w'. */
661 /* The O_SIZE objects have a dual identity. */
662 enum howend
{ SIZE
, LAST
} howend
= SIZE
;
664 object_t
*world
= nil
;
665 object_t
*curobj
= nil
;
667 object_t
*newobject(objtype_t type
, int flags
, int row
, int col
, int len
)
668 /* Make a new object given a type, flags, position and length on the screen. */
671 object_t
**aop
= &world
;
673 new= alloc(sizeof(*new));
690 unsigned long entry2base(struct part_entry
*pe
)
691 /* Return the base sector of the partition if defined. */
693 return pe
->sysind
== NO_PART
? 0 : pe
->lowsec
;
696 unsigned long entry2last(struct part_entry
*pe
)
698 return pe
->sysind
== NO_PART
? -1 : pe
->lowsec
+ pe
->size
- 1;
701 unsigned long entry2size(struct part_entry
*pe
)
703 return pe
->sysind
== NO_PART
? 0 : pe
->size
;
706 int overlap(unsigned long sec
)
707 /* See if sec is part of another partition. */
709 struct part_entry
*pe
;
711 for (pe
= table
+ 1; pe
<= table
+ NR_PARTITIONS
; pe
++) {
712 if (pe
->sysind
== NO_PART
) continue;
714 if (pe
->lowsec
< sec
&& sec
< pe
->lowsec
+ pe
->size
)
720 int aligned(unsigned long sec
, unsigned unit
)
721 /* True if sec is aligned to unit or if it is no problem if it is unaligned. */
723 return (offset
!= 0 && extbase
== 0) || (sec
% unit
== 0);
726 void print(object_t
*op
)
727 /* Print an object's value if it changed. */
729 struct part_entry
*pe
= op
->entry
;
736 /* Remember the old flags and value. */
738 strcpy(oldvalue
, op
->value
);
740 op
->flags
&= ~(OF_ODD
| OF_BAD
);
745 static struct field
{ int type
; char *name
; } fields
[]= {
746 { O_DEV
, "Select device" },
747 { O_NUM
, "Active flag" },
748 { O_TYPHEX
, "Hex partition type" },
749 { O_TYPTXT
, "Partition type" },
750 { O_SCYL
, "Start cylinder" },
751 { O_SHEAD
, "Start head" },
752 { O_SSEC
, "Start sector" },
753 { O_CYL
, "Number of cylinders" },
754 { O_HEAD
, "Number of heads" },
755 { O_SEC
, "Sectors per track" },
756 { O_LCYL
, "Last cylinder" },
757 { O_LHEAD
, "Last head" },
758 { O_LSEC
, "Last sector" },
759 { O_BASE
, "Base sector" },
760 { O_SIZE
, "Size in sectors" },
761 { O_KB
, "Size in kilobytes" },
764 struct field
*fp
= fields
;
766 while (fp
->type
>= 0 && fp
->type
!= curobj
->type
) fp
++;
767 strcpy(op
->value
, fp
->name
);
771 /* Simple text field. */
772 strcpy(op
->value
, op
->text
);
776 /* Name of currently edited device. */
777 name
= op
->type
== O_DEV
? curdev
->name
:
778 offset
== 0 ? "" : curdev
->subname
;
779 if ((n
= strlen(name
)) < op
->len
) n
= op
->len
;
780 strcpy(op
->value
, name
+ (n
- op
->len
));
781 if (device
< 0 && op
->type
== O_DEV
) op
->flags
|= OF_BAD
;
784 /* Position and active flag. */
785 sprintf(op
->value
, "%d%c", (int) (pe
- table
- 1),
786 pe
->bootind
& ACTIVE_FLAG
? '*' : ' ');
789 /* Position if the driver sorts the table. */
790 sprintf(op
->value
, "%s%d",
791 curdev
->parttype
>= PRIMARY
? "p" :
792 curdev
->parttype
== SUBPART
? "s" : "",
793 (curdev
->parttype
== SUBPART
||
794 curdev
->parttype
== FLOPPY
? pe
- table
795 : sort_index
[pe
- table
]) - 1);
798 /* Hex partition type indicator. */
799 sprintf(op
->value
, "%02X", pe
->sysind
);
802 /* Ascii partition type indicator. */
803 strcpy(op
->value
, typ2txt(pe
->sysind
));
806 /* Partition's start cylinder. */
807 sprintf(op
->value
, "%lu", entry2base(pe
) / secpcyl
);
812 sprintf(op
->value
, "%lu", t
% secpcyl
/ sectors
);
813 if (!aligned(t
, secpcyl
) && t
!= table
[0].lowsec
+ sectors
)
819 sprintf(op
->value
, "%lu", t
% sectors
);
820 if (!aligned(t
, sectors
)) op
->flags
|= OF_ODD
;
823 /* Number of cylinders. */
824 sprintf(op
->value
, "%u", cylinders
);
827 /* Number of heads. */
828 sprintf(op
->value
, "%u", heads
);
831 /* Number of sectors per track. */
832 sprintf(op
->value
, "%u", sectors
);
835 /* Partition's last cylinder. */
837 sprintf(op
->value
, "%lu", t
== -1 ? 0 : t
/ secpcyl
);
840 /* Partition's last head. */
842 sprintf(op
->value
, "%lu", t
== -1 ? 0 : t
% secpcyl
/ sectors
);
843 if (!aligned(t
+ 1, secpcyl
)) op
->flags
|= OF_ODD
;
846 /* Partition's last sector. */
848 if (t
== -1) strcpy(op
->value
, "-1");
849 else sprintf(op
->value
, "%lu", t
% sectors
);
850 if (!aligned(t
+ 1, sectors
)) op
->flags
|= OF_ODD
;
853 /* Partition's base sector. */
854 sprintf(op
->value
, "%lu", entry2base(pe
));
855 if (pe
->sysind
!= NO_PART
&& pe
!= &table
[0]
856 && (pe
->lowsec
<= table
[0].lowsec
|| overlap(pe
->lowsec
)))
860 /* Size of partitition in sectors. */
861 t
= howend
== SIZE
? entry2size(pe
) : entry2last(pe
);
862 sprintf(op
->value
, "%lu", pe
->sysind
== NO_PART
? 0 : t
);
863 if (pe
->sysind
!= NO_PART
&& (pe
->size
== 0
864 || pe
->lowsec
+ pe
->size
> table
[0].lowsec
+ table
[0].size
865 || overlap(pe
->lowsec
+ pe
->size
)))
869 /* Size of partitition in kilobytes. */
870 sprintf(op
->value
, "%lu", entry2size(pe
) / 2);
873 sprintf(op
->value
, "?? %d ??", op
->type
);
876 if (device
< 0 && computed(op
->type
)) strcpy(op
->value
, "?");
878 /* If a value overflows the print field then show a blank
879 * reverse video field.
881 if ((n
= strlen(op
->value
)) > op
->len
) {
886 /* Right or left justified? */
887 if (rjust(op
->type
)) {
888 memmove(op
->value
+ (op
->len
- n
), op
->value
, n
);
889 memset(op
->value
, ' ', op
->len
- n
);
891 memset(op
->value
+ n
, ' ', op
->len
- n
);
893 op
->value
[(int) op
->len
]= 0;
895 if ((op
->flags
& (OF_ODD
| OF_BAD
)) == (oldflags
& (OF_ODD
| OF_BAD
))
896 && strcmp(op
->value
, oldvalue
) == 0) {
897 /* The value did not change. */
901 set_cursor(op
->row
, rjust(op
->type
) ? op
->col
- (op
->len
-1) : op
->col
);
903 if (op
->flags
& OF_BAD
) tputs(t_so
, 1, putchr
);
905 if (op
->flags
& OF_ODD
) tputs(t_md
, 1, putchr
);
907 if (op
->flags
& OF_BAD
) tputs(t_se
, 1, putchr
);
909 if (op
->flags
& OF_ODD
) tputs(t_me
, 1, putchr
);
913 /* Repaint all objects that changed. */
917 for (op
= world
; op
!= nil
; op
= op
->next
) print(op
);
920 int typing
; /* Set if a digit has been typed to set a value. */
921 int magic
; /* Changes when using the magic key. */
923 void event(int ev
, object_t
*op
);
925 void m_redraw(int ev
, object_t
*op
)
926 /* Redraw the screen. */
930 if (ev
!= ctrl('L')) return;
933 for (op2
= world
; op2
!= nil
; op2
= op2
->next
) op2
->value
[0]= 0;
936 void m_toggle(int ev
, object_t
*op
)
937 /* Toggle between the driver and alternate geometry. */
941 if (ev
!= 'X') return;
942 if (alt_cyls
== cylinders
&& alt_heads
== heads
&& alt_secs
== sectors
)
945 t
= cylinders
; cylinders
= alt_cyls
; alt_cyls
= t
;
946 t
= heads
; heads
= alt_heads
; alt_heads
= t
;
947 t
= sectors
; sectors
= alt_secs
; alt_secs
= t
;
952 char size_last
[]= "Size";
954 void m_orientation(int ev
, object_t
*op
)
956 if (ev
!= ' ') return;
961 strcpy(size_last
, "Last");
965 strcpy(size_last
, "Size");
969 void m_move(int ev
, object_t
*op
)
970 /* Move to the nearest modifiably object in the intended direction. Objects
971 * on the same row or column are really near.
974 object_t
*near
, *op2
;
975 unsigned dist
, d2
, dr
, dc
;
977 if (ev
!= 'h' && ev
!= 'j' && ev
!= 'k' && ev
!= 'l' && ev
!= 'H')
981 /* No device open? Then try to read first. */
983 if (device
< 0) return;
989 for (op2
= world
; op2
!= nil
; op2
= op2
->next
) {
990 if (op2
== op
|| !(op2
->flags
& OF_MOD
)) continue;
992 dr
= abs(op2
->row
- op
->row
);
993 dc
= abs(op2
->col
- op
->col
);
995 d2
= 25*dr
*dr
+ dc
*dc
;
996 if (op2
->row
!= op
->row
&& op2
->col
!= op
->col
) d2
+= 1000;
1000 if (op2
->col
>= op
->col
) d2
= -1;
1002 case 'j': /* Down */
1003 if (op2
->row
<= op
->row
) d2
= -1;
1006 if (op2
->row
>= op
->row
) d2
= -1;
1008 case 'l': /* Right */
1009 if (op2
->col
<= op
->col
) d2
= -1;
1011 case 'H': /* Home */
1012 if (op2
->type
== O_DEV
) d2
= 0;
1014 if (d2
< dist
) { near
= op2
; dist
= d2
; }
1016 if (near
!= op
) event(E_LEAVE
, op
);
1017 event(E_ENTER
, near
);
1020 void m_updown(int ev
, object_t
*op
)
1021 /* Move a partition table entry up or down. */
1024 struct part_entry tmp
;
1027 if (ev
!= ctrl('K') && ev
!= ctrl('J')) return;
1028 if (op
->entry
== nil
) return;
1030 i
= op
->entry
- table
;
1031 if (ev
== ctrl('K')) {
1035 if (i
>= NR_PARTITIONS
) return;
1039 tmp
= table
[i
]; table
[i
]= table
[j
]; table
[j
]= tmp
;
1040 tmpx
= existing
[i
]; existing
[i
]= existing
[j
]; existing
[j
]= tmpx
;
1043 event(ev
== ctrl('K') ? 'k' : 'j', op
);
1046 void m_enter(int ev
, object_t
*op
)
1047 /* We've moved onto this object. */
1049 if (ev
!= E_ENTER
&& ev
!= ' ' && ev
!= '<' && ev
!= '>' && ev
!= 'X')
1056 void m_leave(int ev
, object_t
*op
)
1057 /* About to leave this object. */
1059 if (ev
!= E_LEAVE
) return;
1062 int within(unsigned *var
, unsigned low
, unsigned value
, unsigned high
)
1063 /* Only set *var to value if it looks reasonable. */
1065 if (low
<= value
&& value
<= high
) {
1072 int lwithin(unsigned long *var
, unsigned long low
, unsigned long value
,
1075 if (low
<= value
&& value
<= high
) {
1082 int nextdevice(object_t
*op
, int delta
)
1083 /* Select the next or previous device from the device list. */
1087 if (offset
!= 0) return 0;
1088 if (dirty
) event(E_WRITE
, op
);
1089 if (dirty
) return 0;
1092 (void) close(device
);
1100 curdev
= curdev
->prev
;
1101 while (delta
< -1 && major(curdev
->rdev
) == major(rdev
)
1102 && curdev
->rdev
< rdev
);
1105 curdev
= curdev
->next
;
1106 while (delta
> 1 && major(curdev
->rdev
) == major(rdev
)
1107 && curdev
->rdev
> rdev
);
1112 void check_ind(struct part_entry
*pe
)
1113 /* If there are no other partitions then make this new one active. */
1115 struct part_entry
*pe2
;
1117 if (pe
->sysind
!= NO_PART
) return;
1119 for (pe2
= table
+ 1; pe2
< table
+ 1 + NR_PARTITIONS
; pe2
++)
1120 if (pe2
->sysind
!= NO_PART
|| pe2
->bootind
& ACTIVE_FLAG
) break;
1122 if (pe2
== table
+ 1 + NR_PARTITIONS
) pe
->bootind
= ACTIVE_FLAG
;
1125 int check_existing(struct part_entry
*pe
)
1126 /* Check and if not ask if an existing partition may be modified. */
1128 static int expert
= 0;
1131 if (expert
|| pe
== nil
|| !existing
[pe
- table
]) return 1;
1134 putstr("Do you wish to modify existing partitions? (y/n) ");
1136 while ((c
= getchar()) != 'y' && c
!= 'n') {}
1139 return (expert
= (c
== 'y'));
1142 void m_modify(int ev
, object_t
*op
)
1143 /* Increment, decrement, set, or toggle the value of an object, using
1144 * arithmetic tricks the author doesn't understand either.
1148 struct part_entry
*pe
= op
->entry
;
1151 unsigned long surplus
;
1152 int radix
= op
->type
== O_TYPHEX
? 0x10 : 10;
1155 if (device
< 0 && op
->type
!= O_DEV
) return;
1159 mul
= radix
; delta
= -1; typing
= 0;
1162 mul
= radix
; delta
= 1; typing
= 0;
1165 if (!typing
) return;
1172 if ('0' <= ev
&& ev
<= '9')
1175 if (radix
== 0x10 && 'a' <= ev
&& ev
<= 'f')
1176 delta
= ev
- 'a' + 10;
1178 if (radix
== 0x10 && 'A' <= ev
&& ev
<= 'F')
1179 delta
= ev
- 'A' + 10;
1183 mul
= typing
? radix
*radix
: 0;
1188 if (!check_existing(pe
)) return;
1192 if (ev
!= '-' && ev
!= '+') return;
1193 if (!nextdevice(op
, delta
)) return;
1196 if (!within(&cylinders
, 1,
1197 cylinders
* mul
/ radix
+ delta
, 1024)) return;
1201 if (!within(&heads
, 1, heads
* mul
/ radix
+ delta
, 255))
1206 if (!within(§ors
, 1, sectors
* mul
/ radix
+ delta
, 63))
1211 if (ev
!= '-' && ev
!= '+') return;
1212 for (op2
= world
; op2
!= nil
; op2
= op2
->next
) {
1213 if (op2
->type
== O_NUM
&& ev
== '+')
1214 op2
->entry
->bootind
= 0;
1216 op
->entry
->bootind
= ev
== '+' ? ACTIVE_FLAG
: 0;
1220 pe
->sysind
= pe
->sysind
* mul
/ radix
+ delta
;
1223 if (ev
!= '-' && ev
!= '+') return;
1225 pe
->sysind
= round_sysind(pe
->sysind
, delta
);
1232 if (op
->type
!= O_SCYL
&& ev
!= '-' && ev
!= '+') return;
1234 if (pe
->sysind
== NO_PART
) memset(pe
, 0, sizeof(*pe
));
1237 if (!lwithin(&t
, 0L,
1238 (t
/ level
* mul
/ radix
+ delta
) * level
+ surplus
,
1240 if (howend
== LAST
|| op
->type
!= O_BASE
)
1241 pe
->size
-= t
- pe
->lowsec
;
1244 if (pe
->sysind
== NO_PART
) pe
->sysind
= MINIX_PART
;
1251 if (op
->type
!= O_LCYL
&& ev
!= '-' && ev
!= '+') return;
1253 if (pe
->sysind
== NO_PART
) memset(pe
, 0, sizeof(*pe
));
1254 t
= pe
->lowsec
+ pe
->size
- 1 + level
;
1255 surplus
= t
% level
- mul
/ radix
* level
;
1256 if (!lwithin(&t
, 0L,
1257 (t
/ level
* mul
/ radix
+ delta
) * level
+ surplus
,
1259 pe
->size
= t
- pe
->lowsec
+ 1;
1261 if (pe
->sysind
== NO_PART
) pe
->sysind
= MINIX_PART
;
1265 if (mul
== 0) pe
->size
= 0; /* new value, no surplus */
1267 if (pe
->sysind
== NO_PART
) {
1268 if (op
->type
== O_KB
|| howend
== SIZE
) {
1269 /* First let loose magic to set the base. */
1276 memset(pe
, 0, sizeof(*pe
));
1278 t
= (op
->type
== O_KB
|| howend
== SIZE
) ? pe
->size
1279 : pe
->lowsec
+ pe
->size
- 1;
1281 if (!lwithin(&t
, 0L,
1282 (t
/ level
* mul
/ radix
+ delta
) * level
+ surplus
,
1284 pe
->size
= (op
->type
== O_KB
|| howend
== SIZE
) ? t
:
1287 if (pe
->sysind
== NO_PART
) pe
->sysind
= MINIX_PART
;
1293 /* The order among the entries may have changed. */
1298 unsigned long spell
[3 + 4 * (1+NR_PARTITIONS
)];
1302 void newspell(unsigned long charm
)
1303 /* Add a new spell, descending order for the base, ascending for the size. */
1307 if (charm
- table
[0].lowsec
> table
[0].size
) return;
1309 for (i
= 0; i
< nspells
; i
++) {
1310 if (charm
== spell
[i
]) return; /* duplicate */
1312 if (touching
== O_BASE
) {
1313 if (charm
== table
[0].lowsec
+ table
[0].size
) return;
1314 if ((spell
[0] - charm
) < (spell
[0] - spell
[i
])) break;
1316 if (charm
== table
[0].lowsec
) return;
1317 if ((charm
- spell
[0]) < (spell
[i
] - spell
[0])) break;
1320 for (j
= ++nspells
; j
> i
; j
--) spell
[j
]= spell
[j
-1];
1324 void m_magic(int ev
, object_t
*op
)
1325 /* Apply magic onto a base or size number. */
1327 struct part_entry
*pe
= op
->entry
, *pe2
;
1328 int rough
= (offset
!= 0 && extbase
== 0);
1330 if (ev
!= 'm' || device
< 0) return;
1333 if (!check_existing(pe
)) return;
1336 /* See what magic we can let loose on this value. */
1339 /* First spell, the current value. */
1342 case O_SHEAD
: /* Start of partition. */
1346 spell
[0]= pe
->lowsec
;
1350 case O_LSEC
: /* End of partition. */
1354 spell
[0]= pe
->lowsec
+ pe
->size
;
1359 if (pe
->sysind
== NO_PART
) {
1360 memset(pe
, 0, sizeof(*pe
));
1362 pe
->sysind
= MINIX_PART
;
1364 if (touching
== O_SIZE
) {
1365 /* First let loose magic on the base. */
1368 for (op2
= world
; op2
!= nil
; op2
= op2
->next
) {
1369 if (op2
->row
== op
->row
&&
1370 op2
->type
== O_BASE
) {
1379 /* Avoid the first sector on the device. */
1380 if (spell
[0] == table
[0].lowsec
) newspell(spell
[0] + 1);
1382 /* Further interesting values are the the bases of other
1383 * partitions or their ends.
1385 for (pe2
= table
; pe2
< table
+ 1 + NR_PARTITIONS
; pe2
++) {
1386 if (pe2
== pe
|| pe2
->sysind
== NO_PART
) continue;
1387 if (pe2
->lowsec
== table
[0].lowsec
)
1388 newspell(table
[0].lowsec
+ 1);
1390 newspell(pe2
->lowsec
);
1391 newspell(pe2
->lowsec
+ pe2
->size
);
1392 if (touching
== O_BASE
&& howend
== SIZE
) {
1393 newspell(pe2
->lowsec
- pe
->size
);
1394 newspell(pe2
->lowsec
+ pe2
->size
- pe
->size
);
1396 if (pe2
->lowsec
% sectors
!= 0) rough
= 1;
1398 /* Present values rounded up to the next cylinder unless
1399 * the table is already a mess. Use "start + 1 track" instead
1400 * of "start + 1 cylinder". Also add the end of the last
1404 unsigned long n
= spell
[0];
1405 if (n
== table
[0].lowsec
) n
++;
1406 n
= (n
+ sectors
- 1) / sectors
* sectors
;
1407 if (n
!= table
[0].lowsec
+ sectors
)
1408 n
= (n
+ secpcyl
- 1) / secpcyl
* secpcyl
;
1410 if (touching
== O_SIZE
)
1411 newspell(table
[0].size
/ secpcyl
* secpcyl
);
1414 /* Magic has been applied, a spell needs to be chosen. */
1416 if (++magic
== nspells
) magic
= 0;
1418 if (touching
== O_BASE
) {
1419 if (howend
== LAST
) pe
->size
-= spell
[magic
] - pe
->lowsec
;
1420 pe
->lowsec
= spell
[magic
];
1422 pe
->size
= spell
[magic
] - pe
->lowsec
;
1424 /* The order among the entries may have changed. */
1429 typedef struct diving
{
1431 struct part_entry old0
;
1433 parttype_t oldparttype
;
1434 unsigned long oldoffset
;
1435 unsigned long oldextbase
;
1438 diving_t
*diving
= nil
;
1440 void m_in(int ev
, object_t
*op
)
1441 /* Go down into a primary or extended partition. */
1444 struct part_entry
*pe
= op
->entry
, ext
;
1447 if (ev
!= '>' || device
< 0 || pe
== nil
|| pe
== &table
[0]
1448 || (!(pe
->sysind
== MINIX_PART
&& offset
== 0)
1449 && !ext_part(pe
->sysind
))
1450 || pe
->size
== 0) return;
1453 if (extbase
!= 0) ext
.size
= extbase
+ extsize
- ext
.lowsec
;
1455 if (dirty
) event(E_WRITE
, op
);
1457 if (device
>= 0) { close(device
); device
= -1; }
1459 newdiv
= alloc(sizeof(*newdiv
));
1460 newdiv
->old0
= table
[0];
1461 newdiv
->oldsubname
= curdev
->subname
;
1462 newdiv
->oldparttype
= curdev
->parttype
;
1463 newdiv
->oldoffset
= offset
;
1464 newdiv
->oldextbase
= extbase
;
1470 n
= strlen(diving
->oldsubname
);
1471 curdev
->subname
= alloc((n
+ 3) * sizeof(curdev
->subname
[0]));
1472 strcpy(curdev
->subname
, diving
->oldsubname
);
1473 curdev
->subname
[n
++]= ':';
1474 curdev
->subname
[n
++]= '0' + (pe
- table
- 1);
1475 curdev
->subname
[n
]= 0;
1477 curdev
->parttype
= curdev
->parttype
== PRIMARY
? SUBPART
: DUNNO
;
1479 if (ext_part(ext
.sysind
) && extbase
== 0) {
1480 extbase
= ext
.lowsec
;
1482 curdev
->parttype
= DUNNO
;
1489 void m_out(int ev
, object_t
*op
)
1490 /* Go up from an extended or subpartition table to its enclosing. */
1494 if (ev
!= '<' || diving
== nil
) return;
1496 if (dirty
) event(E_WRITE
, op
);
1498 if (device
>= 0) { close(device
); device
= -1; }
1503 table
[0]= olddiv
->old0
;
1505 free(curdev
->subname
);
1506 curdev
->subname
= olddiv
->oldsubname
;
1508 curdev
->parttype
= olddiv
->oldparttype
;
1509 offset
= olddiv
->oldoffset
;
1510 extbase
= olddiv
->oldextbase
;
1515 if (diving
== nil
) submerged
= 0; /* We surfaced. */
1518 void installboot(unsigned char *bootblock
, char *masterboot
)
1519 /* Install code from a master bootstrap into a boot block. */
1522 unsigned char buf
[SECTOR_SIZE
];
1526 if ((mfp
= fopen(masterboot
, "r")) == nil
) {
1527 err
= strerror(errno
);
1531 n
= fread(buf
, sizeof(char), SECTOR_SIZE
, mfp
);
1533 err
= strerror(errno
);
1538 err
= "Is probably not a boot sector, too small";
1542 else if (n
< SECTOR_SIZE
&& n
> PART_TABLE_OFF
) {
1543 /* if only code, it cannot override partition table */
1544 err
= "Does not fit in a boot sector";
1548 else if (n
== SECTOR_SIZE
) {
1549 if (buf
[510] != 0x55 || buf
[511] != 0xaa) {
1550 err
= "Is not a boot sector (bad magic)";
1557 if (n
> PART_TABLE_OFF
) {
1558 err
= "Does not fit in a boot sector";
1563 memcpy(bootblock
, buf
, n
);
1566 /* Bootstrap installed. */
1571 printf("%s: %s", masterboot
, err
);
1575 ssize_t
boot_readwrite(int rw
)
1576 /* Read (0) or write (1) the boot sector. */
1580 if (lseek64(device
, (u64_t
) offset
* SECTOR_SIZE
, SEEK_SET
, NULL
) < 0)
1584 case 0: r
= read(device
, bootblock
, SECTOR_SIZE
); break;
1585 case 1: r
= write(device
, bootblock
, SECTOR_SIZE
); break;
1591 void m_read(int ev
, object_t
*op
)
1592 /* Read the partition table from the current device. */
1595 struct part_entry
*pe
;
1597 if (ev
!= 'r' || device
>= 0) return;
1599 /* Open() may cause kernel messages: */
1603 if (((device
= open(curdev
->name
, mode
= O_RDWR
, 0666)) < 0
1605 || (device
= open(curdev
->name
, mode
= O_RDONLY
)) < 0))
1608 printf("%s: %s", curdev
->name
, strerror(errno
));
1610 if (device
>= 0) { close(device
); device
= -1; }
1614 /* Assume up to five lines of kernel messages. */
1618 if (mode
== O_RDONLY
) {
1620 printf("%s: Readonly", curdev
->name
);
1623 memset(bootblock
, 0, sizeof(bootblock
));
1625 n
= boot_readwrite(0);
1627 if (n
<= 0) stat_start(1);
1629 printf("%s: %s", curdev
->name
, strerror(errno
));
1633 if (n
< SECTOR_SIZE
) printf("%s: Unexpected EOF", curdev
->subname
);
1634 if (n
<= 0) stat_end(5);
1636 if (n
< SECTOR_SIZE
) n
= SECTOR_SIZE
;
1638 memcpy(table
+1, bootblock
+PART_TABLE_OFF
,
1639 NR_PARTITIONS
* sizeof(table
[1]));
1640 for (i
= 1; i
<= NR_PARTITIONS
; i
++) {
1641 if ((table
[i
].bootind
& ~ACTIVE_FLAG
) != 0) break;
1643 if (i
<= NR_PARTITIONS
|| bootblock
[510] != 0x55
1644 || bootblock
[511] != 0xAA) {
1645 /* Invalid boot block, install bootstrap, wipe partition table.
1647 memset(bootblock
, 0, sizeof(bootblock
));
1648 installboot(bootblock
, MASTERBOOT
);
1649 memset(table
+1, 0, NR_PARTITIONS
* sizeof(table
[1]));
1651 printf("%s: Invalid partition table (reset)", curdev
->subname
);
1655 /* Fix an extended partition table up to something mere mortals can
1656 * understand. Record already defined partitions.
1658 for (i
= 1; i
<= NR_PARTITIONS
; i
++) {
1660 if (extbase
!= 0 && pe
->sysind
!= NO_PART
)
1661 pe
->lowsec
+= ext_part(pe
->sysind
) ? extbase
: offset
;
1662 existing
[i
]= pe
->sysind
!= NO_PART
;
1667 /* Warn about grave dangers ahead. */
1670 printf("Warning: You are in an extended partition.");
1675 void m_write(int ev
, object_t
*op
)
1676 /* Write the partition table back if modified. */
1679 struct part_entry new_table
[NR_PARTITIONS
], *pe
;
1681 if (ev
!= 'w' && ev
!= E_WRITE
) return;
1682 if (device
< 0) { dirty
= 0; return; }
1686 printf("%s is not changed, or has already been written",
1693 if (bootblock
[510] != 0x55 || bootblock
[511] != 0xAA) {
1694 /* Invalid boot block, warn user. */
1696 printf("Warning: About to write a new table on %s",
1701 /* Will this stop the luser? Probably not... */
1703 printf("You have changed an extended partition. Bad Idea.");
1707 putstr("Save partition table? (y/n) ");
1710 while ((c
= getchar()) != 'y' && c
!= 'n' && c
!= ctrl('?')) {}
1712 if (c
== ctrl('?')) putstr("DEL"); else putchr(c
);
1714 if (c
== 'n' && ev
== E_WRITE
) dirty
= 0;
1715 if (c
!= 'y') return;
1717 memcpy(new_table
, table
+1, NR_PARTITIONS
* sizeof(table
[1]));
1718 for (pe
= new_table
; pe
< new_table
+ NR_PARTITIONS
; pe
++) {
1719 if (pe
->sysind
== NO_PART
) {
1720 memset(pe
, 0, sizeof(*pe
));
1722 abs2dos(&pe
->start_head
, pe
->lowsec
);
1723 abs2dos(&pe
->last_head
, pe
->lowsec
+ pe
->size
- 1);
1725 /* Fear and loathing time: */
1727 pe
->lowsec
-= ext_part(pe
->sysind
)
1731 memcpy(bootblock
+PART_TABLE_OFF
, new_table
, sizeof(new_table
));
1732 bootblock
[510]= 0x55;
1733 bootblock
[511]= 0xAA;
1735 if (boot_readwrite(1) < 0) {
1737 printf("%s: %s", curdev
->name
, strerror(errno
));
1744 void m_shell(int ev
, object_t
*op
)
1745 /* Shell escape, to do calculations for instance. */
1748 void (*sigint
)(int), (*sigquit
)(int), (*sigterm
)(int);
1750 if (ev
!= 's') return;
1755 switch (pid
= fork()) {
1758 printf("can't fork: %s\n", strerror(errno
));
1762 if (device
>= 0) (void) close(device
);
1763 execl("/bin/sh", "sh", (char *) nil
);
1766 printf("/bin/sh: %s\n", strerror(errno
));
1770 sigint
= signal(SIGINT
, SIG_IGN
);
1771 sigquit
= signal(SIGQUIT
, SIG_IGN
);
1772 sigterm
= signal(SIGTERM
, SIG_IGN
);
1773 while (pid
>= 0 && (r
= wait(&status
)) >= 0 && r
!= pid
) {}
1774 (void) signal(SIGINT
, sigint
);
1775 (void) signal(SIGQUIT
, sigquit
);
1776 (void) signal(SIGTERM
, sigterm
);
1781 if (WIFEXITED(status
) && WEXITSTATUS(status
) == 127)
1782 stat_start(0); /* Match the stat_start in the child. */
1784 event(ctrl('L'), op
);
1787 void m_dump(int ev
, object_t
*op
)
1788 /* Raw dump of the partition table. */
1790 struct part_entry table
[NR_PARTITIONS
], *pe
;
1794 if (ev
!= 'p' || device
< 0) return;
1796 memcpy(table
, bootblock
+PART_TABLE_OFF
,
1797 NR_PARTITIONS
* sizeof(table
[0]));
1798 for (i
= 0; i
< NR_PARTITIONS
; i
++) {
1801 dos2chs(&pe
->start_head
, chs
);
1802 printf("%2d%c %02X%15d%5d%4d",
1804 pe
->bootind
& ACTIVE_FLAG
? '*' : ' ',
1806 chs
[0], chs
[1], chs
[2]);
1807 dos2chs(&pe
->last_head
, chs
);
1808 printf("%6d%5d%4d%10lu%10ld%9lu",
1809 chs
[0], chs
[1], chs
[2],
1811 howend
== SIZE
? pe
->size
: pe
->size
+ pe
->lowsec
- 1,
1816 printf("(Raw dump of the original %.40s table)",
1823 void m_quit(int ev
, object_t
*op
)
1824 /* Write the partition table if modified and exit. */
1826 if (ev
!= 'q' && ev
!= 'x') return;
1830 if (dirty
) event(E_WRITE
, op
);
1831 if (dirty
) quitting
= 0;
1834 void m_help(int ev
, object_t
*op
)
1835 /* For people without a clue; let's hope they can find the '?' key. */
1837 static struct help
{
1841 { "? !", "This help / more advice!" },
1842 { "+ - (= _ PgUp PgDn)","Select/increment/decrement/make active" },
1843 { "0-9 (a-f)", "Enter value" },
1844 { "hjkl (arrow keys)", "Move around" },
1845 { "CTRL-K CTRL-J", "Move entry up/down" },
1846 { "CTRL-L", "Redraw screen" },
1847 { ">", "Start a subpartition table" },
1848 { "<", "Back to the primary partition table" },
1849 { "m", "Cycle through magic values" },
1850 { "spacebar", "Show \"Size\" or \"Last\"" },
1851 { "r w", "Read/write partition table" },
1852 { "p s q x", "Raw dump / Shell escape / Quit / Exit" },
1853 { "y n DEL", "Answer \"yes\", \"no\", \"cancel\"" },
1855 static char *advice
[] = {
1856 "* Choose a disk with '+' and '-', then hit 'r'.",
1857 "* To change any value: Move to it and use '+', '-' or type the desired value.",
1858 "* To make a new partition: Move over to the Size or Kb field of an unused",
1859 " partition and type the size. Hit the 'm' key to pad the partition out to",
1860 " a cylinder boundary. Hit 'm' again to pad it out to the end of the disk.",
1861 " You can hit 'm' more than once on a base or size field to see several",
1862 " interesting values go by. Note: Other Operating Systems can be picky about",
1863 " partitions that are not padded to cylinder boundaries. Look for highlighted",
1864 " head or sector numbers.",
1865 "* To reuse a partition: Change the type to MINIX.",
1866 "* To delete a partition: Type a zero in the hex Type field.",
1867 "* To make a partition active: Type '+' in the Num field.",
1868 "* To study the list of keys: Type '?'.",
1874 for (hp
= help
; hp
< arraylimit(help
); hp
++) {
1876 printf("%-25s - %s", hp
->keys
, hp
->what
);
1880 putstr("Things like ");
1881 putstr(t_so
); putstr("this"); putstr(t_se
);
1882 putstr(" must be checked, but ");
1883 putstr(t_md
); putstr("this"); putstr(t_me
);
1884 putstr(" is not really a problem");
1890 for (ap
= advice
; ap
< arraylimit(advice
); ap
++) {
1898 void event(int ev
, object_t
*op
)
1899 /* Simply call all modifiers for an event, each one knows when to act. */
1904 m_orientation(ev
, op
);
1921 /* Get a single keypress. Translate compound keypresses (arrow keys) to
1922 * their simpler equivalents.
1929 set_cursor(curobj
->row
, curobj
->col
);
1933 if (read(0, &ch
, sizeof(ch
)) < 0) fatal("stdin");
1934 c
= (unsigned char) ch
;
1938 case ctrl('['): esc
= 1; break;
1939 case '_': c
= '-'; break;
1940 case '=': c
= '+'; break;
1944 esc
= c
== '[' ? 2 : 0;
1948 case 'D': c
= 'h'; break;
1949 case 'B': c
= 'j'; break;
1950 case 'A': c
= 'k'; break;
1951 case 'C': c
= 'l'; break;
1952 case 'H': c
= 'H'; break;
1954 case 'S': c
= '-'; break;
1956 case 'T': c
= '+'; break;
1965 case ctrl('B'): c
= 'h'; break;
1966 case ctrl('N'): c
= 'j'; break;
1967 case ctrl('P'): c
= 'k'; break;
1968 case ctrl('F'): c
= 'l'; break;
1975 /* Get keypress, handle event, display results, reset screen, ad infinitum. */
1990 int main(int argc
, char **argv
)
1994 struct part_entry
*pe
;
1996 /* Define a few objects to show on the screen. First text: */
1997 op
= newobject(O_INFO
, 0, 0, 2, 19);
1998 op
= newobject(O_TEXT
, 0, 0, 22, 13); op
->text
= "----first----";
1999 op
= newobject(O_TEXT
, 0, 0, 37, 13); op
->text
= "--geom/last--";
2000 op
= newobject(O_TEXT
, 0, 0, 52, 18); op
->text
= "------sectors-----";
2001 op
= newobject(O_TEXT
, 0, 1, 4, 6); op
->text
= "Device";
2002 op
= newobject(O_TEXT
, 0, 1, 23, 12); op
->text
= "Cyl Head Sec";
2003 op
= newobject(O_TEXT
, 0, 1, 38, 12); op
->text
= "Cyl Head Sec";
2004 op
= newobject(O_TEXT
, 0, 1, 56, 4); op
->text
= "Base";
2005 op
= newobject(O_TEXT
, 0, 1, 66, 4); op
->text
= size_last
;
2006 op
= newobject(O_TEXT
, 0, 1, 78, 2); op
->text
= "Kb";
2007 op
= newobject(O_TEXT
, 0, 4, 0, 15); op
->text
= "Num Sort Type";
2009 /* The device is the current object: */
2010 curobj
= newobject(O_DEV
, OF_MOD
, 2, 4, 15);
2011 op
= newobject(O_SUB
, 0, 3, 4, 15);
2014 op
= newobject(O_CYL
, OF_MOD
, 2, 40, 5); op
->entry
= &table
[0];
2015 op
= newobject(O_HEAD
, OF_MOD
, 2, 45, 3); op
->entry
= &table
[0];
2016 op
= newobject(O_SEC
, OF_MOD
, 2, 49, 2); op
->entry
= &table
[0];
2018 /* Objects for the device: */
2019 op
= newobject(O_SCYL
, 0, 3, 25, 5); op
->entry
= &table
[0];
2020 op
= newobject(O_SHEAD
, 0, 3, 30, 3); op
->entry
= &table
[0];
2021 op
= newobject(O_SSEC
, 0, 3, 34, 2); op
->entry
= &table
[0];
2022 op
= newobject(O_LCYL
, 0, 3, 40, 5); op
->entry
= &table
[0];
2023 op
= newobject(O_LHEAD
, 0, 3, 45, 3); op
->entry
= &table
[0];
2024 op
= newobject(O_LSEC
, 0, 3, 49, 2); op
->entry
= &table
[0];
2025 op
= newobject(O_BASE
, 0, 3, 59, 9); op
->entry
= &table
[0];
2026 op
= newobject(O_SIZE
, 0, 3, 69, 9); op
->entry
= &table
[0];
2027 op
= newobject(O_KB
, 0, 3, 79, 9); op
->entry
= &table
[0];
2029 /* Objects for each partition: */
2030 for (r
= 5, pe
= table
+1; pe
<= table
+NR_PARTITIONS
; r
++, pe
++) {
2031 op
= newobject(O_NUM
, OF_MOD
, r
, 1, 2); op
->entry
= pe
;
2032 op
= newobject(O_SORT
, 0, r
, 5, 2); op
->entry
= pe
;
2033 op
= newobject(O_TYPHEX
, OF_MOD
, r
, 10, 2); op
->entry
= pe
;
2034 op
= newobject(O_TYPTXT
, OF_MOD
, r
, 12, 9); op
->entry
= pe
;
2035 op
= newobject(O_SCYL
, OF_MOD
, r
, 25, 5); op
->entry
= pe
;
2036 op
= newobject(O_SHEAD
, OF_MOD
, r
, 30, 3); op
->entry
= pe
;
2037 op
= newobject(O_SSEC
, OF_MOD
, r
, 34, 2); op
->entry
= pe
;
2038 op
= newobject(O_LCYL
, OF_MOD
, r
, 40, 5); op
->entry
= pe
;
2039 op
= newobject(O_LHEAD
, OF_MOD
, r
, 45, 3); op
->entry
= pe
;
2040 op
= newobject(O_LSEC
, OF_MOD
, r
, 49, 2); op
->entry
= pe
;
2041 op
= newobject(O_BASE
, OF_MOD
, r
, 59, 9); op
->entry
= pe
;
2042 op
= newobject(O_SIZE
, OF_MOD
, r
, 69, 9); op
->entry
= pe
;
2043 op
= newobject(O_KB
, OF_MOD
, r
, 79, 9); op
->entry
= pe
;
2046 for (i
= 1; i
< argc
; i
++) newdevice(argv
[i
], 0);
2048 if (firstdev
== nil
) {
2055 if (firstdev
!= nil
) {