SYSENTER/SYSCALL support
[minix.git] / commands / part / part.c
blobc31f3d5833f3d358cb5429e4c66f3da4644083b4
1 /* part 1.57 - Partition table editor Author: Kees J. Bot
2 * 13 Mar 1992
3 * Needs about 22k heap+stack.
4 */
5 #define nil 0
6 #include <sys/types.h>
7 #include <stdio.h>
8 #include <termcap.h>
9 #include <errno.h>
10 #include <unistd.h>
11 #include <stddef.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <signal.h>
15 #include <fcntl.h>
16 #include <time.h>
17 #include <dirent.h>
18 #include <limits.h>
19 #include <sys/stat.h>
20 #include <sys/wait.h>
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>
27 #include <termios.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";
35 /* Template:
36 ----first---- --geom/last-- ------sectors-----
37 Device Cyl Head Sec Cyl Head Sec Base Size Kb
38 /dev/c0d0 977 5 17
39 /dev/c0d0:2 0 0 2 976 4 16 2 83043 41521
40 Num Sort Type
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)
62 report(label);
63 exit(1);
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("");
80 void tty_raw(void)
81 /* Set the terminal to raw mode, no signals, no echoing. */
83 struct termios rawterm;
85 rawterm= termios;
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];
94 int t_li, t_co;
95 #define STATUSROW 10
97 void init_tty(void)
98 /* Get terminal capabilities and set the tty to "editor" mode. */
100 char *term;
101 static char termbuf[1024];
102 char *tp;
104 if ((term= getenv("TERM")) == nil || tgetent(termbuf, term) != 1) {
105 fprintf(stderr, "part: Can't get terminal capabilities\n");
106 exit(1);
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");
111 exit(1);
113 t_li= tgetnum("li");
114 t_co= tgetnum("co");
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));
120 save_ttyflags();
121 tty_raw();
124 void putchr(int c)
126 putchar(c);
129 void putstr(char *s)
131 int c;
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;
142 int stat_ktl= 1;
143 int need_help= 1;
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);
157 stat_ktl= ktl;
158 need_help= 1;
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;
166 need_help= 1;
168 if (need_help && statusrow < (24-2)) {
169 if (statusrow > STATUSROW) stat_start(0);
170 stat_start(0);
171 putstr(
172 "Type '+' or '-' to change, 'r' to read, '?' for more help, 'q' to exit");
174 statusrow= STATUSROW;
175 need_help= 0;
178 void clear_screen(void)
180 set_cursor(0, 0);
181 tputs(t_cd, 1, putchr);
182 stat_ktl= 1;
183 stat_reset();
186 void reset_tty(void)
187 /* Reset the tty to cooked mode. */
189 restore_ttyflags();
190 set_cursor(statusrow, 0);
191 tputs(t_cd, 1, putchr);
194 void *alloc(size_t n)
196 void *m;
198 if ((m= malloc(n)) == nil) { reset_tty(); fatal(""); }
200 return m;
203 #ifndef makedev /* Missing in sys/types.h */
204 #define minor(dev) (((dev) >> MINOR) & BYTE)
205 #define major(dev) (((dev) >> MAJOR) & BYTE)
206 #define makedev(major, minor) \
207 ((dev_t) (((major) << MAJOR) | ((minor) << MINOR)))
208 #endif
210 typedef enum parttype { DUNNO, SUBPART, PRIMARY, FLOPPY } parttype_t;
212 typedef struct device {
213 struct device *next, *prev; /* Circular dequeue. */
214 dev_t rdev; /* Device number (sorting only). */
215 char *name; /* E.g. /dev/c0d0 */
216 char *subname; /* E.g. /dev/c0d0:2 */
217 parttype_t parttype;
218 } device_t;
220 device_t *firstdev= nil, *curdev;
222 void newdevice(char *name, int scanning)
223 /* Add a device to the device list. If scanning is set then we are reading
224 * /dev, so insert the device in device number order and make /dev/c0d0 current.
227 device_t *new, *nextdev, *prevdev;
228 struct stat st;
230 st.st_rdev= 0;
231 if (scanning) {
232 if (stat(name, &st) < 0 || !S_ISBLK(st.st_mode)) return;
234 switch (major(st.st_rdev)) {
235 case 2:
236 /* Floppy */
237 if (minor(st.st_rdev) >= 4) return;
238 break;
239 case 3:
240 case 8:
241 case 10:
242 case 12:
243 case 16:
244 /* Disk controller */
245 if (minor(st.st_rdev) >= 0x80
246 || minor(st.st_rdev) % 5 != 0) return;
247 break;
248 default:
249 return;
251 /* Interesting device found. */
252 } else {
253 (void) stat(name, &st);
256 new= alloc(sizeof(*new));
257 new->rdev= st.st_rdev;
258 new->name= alloc((strlen(name) + 1) * sizeof(new->name[0]));
259 strcpy(new->name, name);
260 new->subname= new->name;
261 new->parttype= DUNNO;
262 if (major(st.st_rdev) == major(DEV_FD0) && minor(st.st_rdev) < 112) {
263 new->parttype= FLOPPY;
264 } else
265 if (st.st_rdev >= DEV_C0D0 && minor(st.st_rdev) < 128
266 && minor(st.st_rdev) % 5 == 0) {
267 new->parttype= PRIMARY;
270 if (firstdev == nil) {
271 firstdev= new;
272 new->next= new->prev= new;
273 curdev= firstdev;
274 return;
276 nextdev= firstdev;
277 while (new->rdev >= nextdev->rdev
278 && (nextdev= nextdev->next) != firstdev) {}
279 prevdev= nextdev->prev;
280 new->next= nextdev;
281 nextdev->prev= new;
282 new->prev= prevdev;
283 prevdev->next= new;
285 if (new->rdev < firstdev->rdev) firstdev= new;
286 if (new->rdev == DEV_C0D0) curdev= new;
287 if (curdev->rdev != DEV_C0D0) curdev= firstdev;
290 void getdevices(void)
291 /* Get all block devices from /dev that look interesting. */
293 DIR *d;
294 struct dirent *e;
295 char name[5 + NAME_MAX + 1];
297 if ((d= opendir("/dev")) == nil) fatal("/dev");
299 while ((e= readdir(d)) != nil) {
300 strcpy(name, "/dev/");
301 strcpy(name + 5, e->d_name);
302 newdevice(name, 1);
304 (void) closedir(d);
307 int dirty= 0;
308 unsigned char bootblock[SECTOR_SIZE];
309 struct part_entry table[1 + NR_PARTITIONS];
310 int existing[1 + NR_PARTITIONS];
311 unsigned long offset= 0, extbase= 0, extsize;
312 int submerged= 0;
313 char sort_index[1 + NR_PARTITIONS];
314 unsigned cylinders= 1, heads= 1, sectors= 1, secpcyl= 1;
315 unsigned alt_cyls= 1, alt_heads= 1, alt_secs= 1;
316 int precise= 0;
317 int device= -1;
319 unsigned long sortbase(struct part_entry *pe)
321 return pe->sysind == NO_PART ? -1 : pe->lowsec;
324 void sort(void)
325 /* Let the sort_index array show the order partitions are sorted in. */
327 int i, j;
328 int idx[1 + NR_PARTITIONS];
330 for (i= 1; i <= NR_PARTITIONS; i++) idx[i]= i;
332 for (i= 1; i <= NR_PARTITIONS; i++) {
333 for (j= 1; j <= NR_PARTITIONS-1; j++) {
334 int sj= idx[j], sj1= idx[j+1];
336 if (sortbase(&table[sj]) > sortbase(&table[sj1])) {
337 idx[j]= sj1;
338 idx[j+1]= sj;
342 for (i= 1; i <= NR_PARTITIONS; i++) sort_index[idx[i]]= i;
345 void dos2chs(unsigned char *dos, unsigned *chs)
346 /* Extract cylinder, head and sector from the three bytes DOS uses to address
347 * a sector. Note that bits 8 & 9 of the cylinder number come from bit 6 & 7
348 * of the sector byte. The sector number is rebased to count from 0.
351 chs[0]= ((dos[1] & 0xC0) << 2) | dos[2];
352 chs[1]= dos[0];
353 chs[2]= (dos[1] & 0x3F) - 1;
356 void abs2dos(unsigned char *dos, unsigned long pos)
357 /* Translate a sector offset to three DOS bytes. */
359 unsigned h, c, s;
361 c= pos / secpcyl;
362 h= (pos % secpcyl) / sectors;
363 s= pos % sectors + 1;
365 dos[0]= h;
366 dos[1]= s | ((c >> 2) & 0xC0);
367 dos[2]= c & 0xFF;
370 void recompute0(void)
371 /* Recompute the partition size for the device after a geometry change. */
373 if (device < 0) {
374 cylinders= heads= sectors= 1;
375 memset(table, 0, sizeof(table));
376 } else
377 if (!precise && offset == 0) {
378 table[0].lowsec= 0;
379 table[0].size= (unsigned long) cylinders * heads * sectors;
381 table[0].sysind= device < 0 ? NO_PART : MINIX_PART;
382 secpcyl= heads * sectors;
385 void guess_geometry(void)
386 /* With a bit of work one can deduce the disk geometry from the partition
387 * table. This may be necessary if the driver gets it wrong. (If partition
388 * tables didn't have C/H/S numbers we would not care at all...)
391 int i, n;
392 struct part_entry *pe;
393 unsigned chs[3];
394 unsigned long sec;
395 unsigned h, s;
396 unsigned char HS[256][8]; /* Bit map off all possible H/S */
398 alt_cyls= alt_heads= alt_secs= 0;
400 /* Initially all possible H/S combinations are possible. HS[h][0]
401 * bit 0 is used to rule out a head value.
403 for (h= 1; h <= 255; h++) {
404 for (s= 0; s < 8; s++) HS[h][s]= 0xFF;
407 for (i= 0; i < 2*NR_PARTITIONS; i++) {
408 pe= &(table+1)[i >> 1];
409 if (pe->sysind == NO_PART) continue;
411 /* Get the end or start sector numbers (in that order). */
412 if ((i & 1) == 0) {
413 dos2chs(&pe->last_head, chs);
414 sec= pe->lowsec + pe->size - 1;
415 } else {
416 dos2chs(&pe->start_head, chs);
417 sec= pe->lowsec;
420 if (chs[0] >= alt_cyls) alt_cyls= chs[0]+1;
422 /* Which H/S combinations can be ruled out? */
423 for (h= 1; h <= 255; h++) {
424 if (HS[h][0] == 0) continue;
425 n = 0;
426 for (s= 1; s <= 63; s++) {
427 if ((chs[0] * h + chs[1]) * s + chs[2] != sec) {
428 HS[h][s/8] &= ~(1 << (s%8));
430 if (HS[h][s/8] & (1 << (s%8))) n++;
432 if (n == 0) HS[h][0]= 0;
436 /* See if only one remains. */
437 i= 0;
438 for (h= 1; h <= 255; h++) {
439 if (HS[h][0] == 0) continue;
440 for (s= 1; s <= 63; s++) {
441 if (HS[h][s/8] & (1 << (s%8))) {
442 i++;
443 alt_heads= h;
444 alt_secs= s;
449 /* Forget it if more than one choice... */
450 if (i > 1) alt_cyls= alt_heads= alt_secs= 0;
453 void geometry(void)
454 /* Find out the geometry of the device by querying the driver, or by looking
455 * at the partition table. These numbers are crosschecked to make sure that
456 * the geometry is correct. Master bootstraps other than the Minix one use
457 * the CHS numbers in the partition table to load the bootstrap of the active
458 * partition.
461 struct stat dst;
462 int err= 0;
463 struct partition geometry;
465 if (submerged) {
466 /* Geometry already known. */
467 sort();
468 return;
470 precise= 0;
471 cylinders= 0;
472 recompute0();
473 if (device < 0) return;
475 /* Try to guess the geometry from the partition table. */
476 guess_geometry();
478 /* Try to get the geometry from the driver. */
479 (void) fstat(device, &dst);
481 if (S_ISBLK(dst.st_mode) || S_ISCHR(dst.st_mode)) {
482 /* Try to get the drive's geometry from the driver. */
484 if (ioctl(device, DIOCGETP, &geometry) < 0)
485 err= errno;
486 else {
487 table[0].lowsec= div64u(geometry.base, SECTOR_SIZE);
488 table[0].size= div64u(geometry.size, SECTOR_SIZE);
489 cylinders= geometry.cylinders;
490 heads= geometry.heads;
491 sectors= geometry.sectors;
492 precise= 1;
494 } else {
495 err= ENODEV;
498 if (err != 0) {
499 /* Getting the geometry from the driver failed, so use the
500 * alternate geometry.
502 if (alt_heads == 0) {
503 alt_cyls= table[0].size / (64 * 32);
504 alt_heads= 64;
505 alt_secs= 32;
508 cylinders= alt_cyls;
509 heads= alt_heads;
510 sectors= alt_secs;
512 stat_start(1);
513 printf("Failure to get the geometry of %s: %s", curdev->name,
514 errno == ENOTTY ? "No driver support" : strerror(err));
515 stat_end(5);
516 stat_start(0);
517 printf("The geometry has been guessed as %ux%ux%u",
518 cylinders, heads, sectors);
519 stat_end(5);
520 } else {
521 if (alt_heads == 0) {
522 alt_cyls= cylinders;
523 alt_heads= heads;
524 alt_secs= sectors;
527 if (heads != alt_heads || sectors != alt_secs) {
528 stat_start(1);
529 printf("WARNING:");
530 stat_end(10);
531 stat_start(0);
532 printf(
533 "The %ux%ux%u geometry obtained from the device driver does not match",
534 cylinders, heads, sectors);
535 stat_end(10);
536 stat_start(0);
537 printf(
538 "the %ux%ux%u geometry implied by the partition table. Hit 'X' to switch",
539 alt_cyls, alt_heads, alt_secs);
540 stat_end(10);
541 stat_start(0);
542 printf(
543 "between the two geometries to see what is best. Note that the geometry");
544 stat_end(10);
545 stat_start(0);
546 printf(
547 "must be correct when the table is written or the system may not boot!");
548 stat_end(10);
552 /* Show the base and size of the device instead of the whole drive.
553 * This makes sense for subpartitioning primary partitions.
555 if (precise && ioctl(device, DIOCGETP, &geometry) >= 0) {
556 table[0].lowsec= div64u(geometry.base, SECTOR_SIZE);
557 table[0].size= div64u(geometry.size, SECTOR_SIZE);
558 } else {
559 precise= 0;
561 recompute0();
562 sort();
565 typedef struct indicators { /* Partition type to partition name. */
566 unsigned char ind;
567 char name[10];
568 } indicators_t;
570 indicators_t ind_table[]= {
571 { 0x00, "None" },
572 { 0x01, "FAT-12" },
573 { 0x02, "XENIX /" },
574 { 0x03, "XENIX usr" },
575 { 0x04, "FAT-16" },
576 { 0x05, "EXTENDED" },
577 { 0x06, "FAT-16" },
578 { 0x07, "HPFS/NTFS" },
579 { 0x08, "AIX" },
580 { 0x09, "COHERENT" },
581 { 0x0A, "OS/2" },
582 { 0x0B, "FAT-32" },
583 { 0x0C, "FAT?" },
584 { 0x0E, "FAT?" },
585 { 0x0F, "EXTENDED" },
586 { 0x10, "OPUS" },
587 { 0x40, "VENIX286" },
588 { 0x42, "W2000 Dyn" },
589 { 0x52, "MICROPORT" },
590 { 0x63, "386/IX" },
591 { 0x64, "NOVELL286" },
592 { 0x65, "NOVELL386" },
593 { 0x75, "PC/IX" },
594 { 0x80, "MINIX-OLD" },
595 { 0x81, "MINIX" },
596 { 0x82, "LINUXswap" },
597 { 0x83, "LINUX" },
598 { 0x93, "AMOEBA" },
599 { 0x94, "AMOEBAbad" },
600 { 0xA5, "386BSD" },
601 { 0xB7, "BSDI" },
602 { 0xB8, "BSDI swap" },
603 { 0xC7, "SYRINX" },
604 { 0xDB, "CPM" },
605 { 0xFF, "BADBLOCKS" },
608 char *typ2txt(int ind)
609 /* Translate a numeric partition indicator for human eyes. */
611 indicators_t *pind;
613 for (pind= ind_table; pind < arraylimit(ind_table); pind++) {
614 if (pind->ind == ind) return pind->name;
616 return "";
619 int round_sysind(int ind, int delta)
620 /* Find the next known partition type starting with ind in direction delta. */
622 indicators_t *pind;
624 ind= (ind + delta) & 0xFF;
626 if (delta < 0) {
627 for (pind= arraylimit(ind_table)-1; pind->ind > ind; pind--) {}
628 } else {
629 for (pind= ind_table; pind->ind < ind; pind++) {}
631 return pind->ind;
634 /* Objects on the screen, either simple pieces of the text or the cylinder
635 * number of the start of partition three.
637 typedef enum objtype {
638 O_INFO, O_TEXT, O_DEV, O_SUB,
639 O_TYPTXT, O_SORT, O_NUM, O_TYPHEX,
640 O_CYL, O_HEAD, O_SEC,
641 O_SCYL, O_SHEAD, O_SSEC, O_LCYL, O_LHEAD, O_LSEC, O_BASE, O_SIZE, O_KB
642 } objtype_t;
644 #define rjust(type) ((type) >= O_TYPHEX)
645 #define computed(type) ((type) >= O_TYPTXT)
647 typedef struct object {
648 struct object *next;
649 objtype_t type; /* Text field, cylinder number, etc. */
650 char flags; /* Modifiable? */
651 char row;
652 char col;
653 char len;
654 struct part_entry *entry; /* What does the object refer to? */
655 char *text;
656 char value[20]; /* Value when printed. */
657 } object_t;
659 #define OF_MOD 0x01 /* Object value is modifiable. */
660 #define OF_ODD 0x02 /* It has a somewhat odd value. */
661 #define OF_BAD 0x04 /* Its value is no good at all. */
663 /* Events: (Keypress events are the value of the key pressed.) */
664 #define E_ENTER (-1) /* Cursor moves onto object. */
665 #define E_LEAVE (-2) /* Cursor leaves object. */
666 #define E_WRITE (-3) /* Write, but not by typing 'w'. */
668 /* The O_SIZE objects have a dual identity. */
669 enum howend { SIZE, LAST } howend= SIZE;
671 object_t *world= nil;
672 object_t *curobj= nil;
674 object_t *newobject(objtype_t type, int flags, int row, int col, int len)
675 /* Make a new object given a type, flags, position and length on the screen. */
677 object_t *new;
678 object_t **aop= &world;
680 new= alloc(sizeof(*new));
682 new->type= type;
683 new->flags= flags;
684 new->row= row;
685 new->col= col;
686 new->len= len;
687 new->entry= nil;
688 new->text= "";
689 new->value[0]= 0;
691 new->next= *aop;
692 *aop= new;
694 return new;
697 unsigned long entry2base(struct part_entry *pe)
698 /* Return the base sector of the partition if defined. */
700 return pe->sysind == NO_PART ? 0 : pe->lowsec;
703 unsigned long entry2last(struct part_entry *pe)
705 return pe->sysind == NO_PART ? -1 : pe->lowsec + pe->size - 1;
708 unsigned long entry2size(struct part_entry *pe)
710 return pe->sysind == NO_PART ? 0 : pe->size;
713 int overlap(unsigned long sec)
714 /* See if sec is part of another partition. */
716 struct part_entry *pe;
718 for (pe= table + 1; pe <= table + NR_PARTITIONS; pe++) {
719 if (pe->sysind == NO_PART) continue;
721 if (pe->lowsec < sec && sec < pe->lowsec + pe->size)
722 return 1;
724 return 0;
727 int aligned(unsigned long sec, unsigned unit)
728 /* True if sec is aligned to unit or if it is no problem if it is unaligned. */
730 return (offset != 0 && extbase == 0) || (sec % unit == 0);
733 void print(object_t *op)
734 /* Print an object's value if it changed. */
736 struct part_entry *pe= op->entry;
737 int n;
738 unsigned long t;
739 char *name;
740 int oldflags;
741 char oldvalue[20];
743 /* Remember the old flags and value. */
744 oldflags= op->flags;
745 strcpy(oldvalue, op->value);
747 op->flags&= ~(OF_ODD | OF_BAD);
749 switch (op->type) {
750 case O_INFO: {
751 /* Current field. */
752 static struct field { int type; char *name; } fields[]= {
753 { O_DEV, "Select device" },
754 { O_NUM, "Active flag" },
755 { O_TYPHEX, "Hex partition type" },
756 { O_TYPTXT, "Partition type" },
757 { O_SCYL, "Start cylinder" },
758 { O_SHEAD, "Start head" },
759 { O_SSEC, "Start sector" },
760 { O_CYL, "Number of cylinders" },
761 { O_HEAD, "Number of heads" },
762 { O_SEC, "Sectors per track" },
763 { O_LCYL, "Last cylinder" },
764 { O_LHEAD, "Last head" },
765 { O_LSEC, "Last sector" },
766 { O_BASE, "Base sector" },
767 { O_SIZE, "Size in sectors" },
768 { O_KB, "Size in kilobytes" },
769 { -1, "?" },
771 struct field *fp= fields;
773 while (fp->type >= 0 && fp->type != curobj->type) fp++;
774 strcpy(op->value, fp->name);
775 op->flags|= OF_ODD;
776 break; }
777 case O_TEXT:
778 /* Simple text field. */
779 strcpy(op->value, op->text);
780 break;
781 case O_DEV:
782 case O_SUB:
783 /* Name of currently edited device. */
784 name= op->type == O_DEV ? curdev->name :
785 offset == 0 ? "" : curdev->subname;
786 if ((n= strlen(name)) < op->len) n= op->len;
787 strcpy(op->value, name + (n - op->len));
788 if (device < 0 && op->type == O_DEV) op->flags|= OF_BAD;
789 break;
790 case O_NUM:
791 /* Position and active flag. */
792 sprintf(op->value, "%d%c", (int) (pe - table - 1),
793 pe->bootind & ACTIVE_FLAG ? '*' : ' ');
794 break;
795 case O_SORT:
796 /* Position if the driver sorts the table. */
797 sprintf(op->value, "%s%d",
798 curdev->parttype >= PRIMARY ? "p" :
799 curdev->parttype == SUBPART ? "s" : "",
800 (curdev->parttype == SUBPART ||
801 curdev->parttype == FLOPPY ? pe - table
802 : sort_index[pe - table]) - 1);
803 break;
804 case O_TYPHEX:
805 /* Hex partition type indicator. */
806 sprintf(op->value, "%02X", pe->sysind);
807 break;
808 case O_TYPTXT:
809 /* Ascii partition type indicator. */
810 strcpy(op->value, typ2txt(pe->sysind));
811 break;
812 case O_SCYL:
813 /* Partition's start cylinder. */
814 sprintf(op->value, "%lu", entry2base(pe) / secpcyl);
815 break;
816 case O_SHEAD:
817 /* Start head. */
818 t= entry2base(pe);
819 sprintf(op->value, "%lu", t % secpcyl / sectors);
820 if (!aligned(t, secpcyl) && t != table[0].lowsec + sectors)
821 op->flags|= OF_ODD;
822 break;
823 case O_SSEC:
824 /* Start sector. */
825 t= entry2base(pe);
826 sprintf(op->value, "%lu", t % sectors);
827 if (!aligned(t, sectors)) op->flags|= OF_ODD;
828 break;
829 case O_CYL:
830 /* Number of cylinders. */
831 sprintf(op->value, "%u", cylinders);
832 break;
833 case O_HEAD:
834 /* Number of heads. */
835 sprintf(op->value, "%u", heads);
836 break;
837 case O_SEC:
838 /* Number of sectors per track. */
839 sprintf(op->value, "%u", sectors);
840 break;
841 case O_LCYL:
842 /* Partition's last cylinder. */
843 t= entry2last(pe);
844 sprintf(op->value, "%lu", t == -1 ? 0 : t / secpcyl);
845 break;
846 case O_LHEAD:
847 /* Partition's last head. */
848 t= entry2last(pe);
849 sprintf(op->value, "%lu", t == -1 ? 0 : t % secpcyl / sectors);
850 if (!aligned(t + 1, secpcyl)) op->flags|= OF_ODD;
851 break;
852 case O_LSEC:
853 /* Partition's last sector. */
854 t= entry2last(pe);
855 sprintf(op->value, t == -1 ? "-1" : "%lu", t % sectors);
856 if (!aligned(t + 1, sectors)) op->flags|= OF_ODD;
857 break;
858 case O_BASE:
859 /* Partition's base sector. */
860 sprintf(op->value, "%lu", entry2base(pe));
861 if (pe->sysind != NO_PART && pe != &table[0]
862 && (pe->lowsec <= table[0].lowsec || overlap(pe->lowsec)))
863 op->flags|= OF_BAD;
864 break;
865 case O_SIZE:
866 /* Size of partitition in sectors. */
867 t= howend == SIZE ? entry2size(pe) : entry2last(pe);
868 sprintf(op->value, "%lu", pe->sysind == NO_PART ? 0 : t);
869 if (pe->sysind != NO_PART && (pe->size == 0
870 || pe->lowsec + pe->size > table[0].lowsec + table[0].size
871 || overlap(pe->lowsec + pe->size)))
872 op->flags|= OF_BAD;
873 break;
874 case O_KB:
875 /* Size of partitition in kilobytes. */
876 sprintf(op->value, "%lu", entry2size(pe) / 2);
877 break;
878 default:
879 sprintf(op->value, "?? %d ??", op->type);
882 if (device < 0 && computed(op->type)) strcpy(op->value, "?");
884 /* If a value overflows the print field then show a blank
885 * reverse video field.
887 if ((n= strlen(op->value)) > op->len) {
888 n= 0;
889 op->flags|= OF_BAD;
892 /* Right or left justified? */
893 if (rjust(op->type)) {
894 memmove(op->value + (op->len - n), op->value, n);
895 memset(op->value, ' ', op->len - n);
896 } else {
897 memset(op->value + n, ' ', op->len - n);
899 op->value[op->len]= 0;
901 if ((op->flags & (OF_ODD | OF_BAD)) == (oldflags & (OF_ODD | OF_BAD))
902 && strcmp(op->value, oldvalue) == 0) {
903 /* The value did not change. */
904 return;
907 set_cursor(op->row, rjust(op->type) ? op->col - (op->len-1) : op->col);
909 if (op->flags & OF_BAD) tputs(t_so, 1, putchr);
910 else
911 if (op->flags & OF_ODD) tputs(t_md, 1, putchr);
912 putstr(op->value);
913 if (op->flags & OF_BAD) tputs(t_se, 1, putchr);
914 else
915 if (op->flags & OF_ODD) tputs(t_me, 1, putchr);
918 void display(void)
919 /* Repaint all objects that changed. */
921 object_t *op;
923 for (op= world; op != nil; op= op->next) print(op);
926 int typing; /* Set if a digit has been typed to set a value. */
927 int magic; /* Changes when using the magic key. */
929 void event(int ev, object_t *op);
931 void m_redraw(int ev, object_t *op)
932 /* Redraw the screen. */
934 object_t *op2;
936 if (ev != ctrl('L')) return;
938 clear_screen();
939 for (op2= world; op2 != nil; op2= op2->next) op2->value[0]= 0;
942 void m_toggle(int ev, object_t *op)
943 /* Toggle between the driver and alternate geometry. */
945 unsigned t;
947 if (ev != 'X') return;
948 if (alt_cyls == cylinders && alt_heads == heads && alt_secs == sectors)
949 return;
951 t= cylinders; cylinders= alt_cyls; alt_cyls= t;
952 t= heads; heads= alt_heads; alt_heads= t;
953 t= sectors; sectors= alt_secs; alt_secs= t;
954 dirty= 1;
955 recompute0();
958 char size_last[]= "Size";
960 void m_orientation(int ev, object_t *op)
962 if (ev != ' ') return;
964 switch (howend) {
965 case SIZE:
966 howend= LAST;
967 strcpy(size_last, "Last");
968 break;
969 case LAST:
970 howend= SIZE;
971 strcpy(size_last, "Size");
975 void m_move(int ev, object_t *op)
976 /* Move to the nearest modifiably object in the intended direction. Objects
977 * on the same row or column are really near.
980 object_t *near, *op2;
981 unsigned dist, d2, dr, dc;
983 if (ev != 'h' && ev != 'j' && ev != 'k' && ev != 'l' && ev != 'H')
984 return;
986 if (device < 0) {
987 /* No device open? Then try to read first. */
988 event('r', op);
989 if (device < 0) return;
992 near= op;
993 dist= -1;
995 for (op2= world; op2 != nil; op2= op2->next) {
996 if (op2 == op || !(op2->flags & OF_MOD)) continue;
998 dr= abs(op2->row - op->row);
999 dc= abs(op2->col - op->col);
1001 d2= 25*dr*dr + dc*dc;
1002 if (op2->row != op->row && op2->col != op->col) d2+= 1000;
1004 switch (ev) {
1005 case 'h': /* Left */
1006 if (op2->col >= op->col) d2= -1;
1007 break;
1008 case 'j': /* Down */
1009 if (op2->row <= op->row) d2= -1;
1010 break;
1011 case 'k': /* Up */
1012 if (op2->row >= op->row) d2= -1;
1013 break;
1014 case 'l': /* Right */
1015 if (op2->col <= op->col) d2= -1;
1016 break;
1017 case 'H': /* Home */
1018 if (op2->type == O_DEV) d2= 0;
1020 if (d2 < dist) { near= op2; dist= d2; }
1022 if (near != op) event(E_LEAVE, op);
1023 event(E_ENTER, near);
1026 void m_updown(int ev, object_t *op)
1027 /* Move a partition table entry up or down. */
1029 int i, j;
1030 struct part_entry tmp;
1031 int tmpx;
1033 if (ev != ctrl('K') && ev != ctrl('J')) return;
1034 if (op->entry == nil) return;
1036 i= op->entry - table;
1037 if (ev == ctrl('K')) {
1038 if (i <= 1) return;
1039 j= i-1;
1040 } else {
1041 if (i >= NR_PARTITIONS) return;
1042 j= i+1;
1045 tmp= table[i]; table[i]= table[j]; table[j]= tmp;
1046 tmpx= existing[i]; existing[i]= existing[j]; existing[j]= tmpx;
1047 sort();
1048 dirty= 1;
1049 event(ev == ctrl('K') ? 'k' : 'j', op);
1052 void m_enter(int ev, object_t *op)
1053 /* We've moved onto this object. */
1055 if (ev != E_ENTER && ev != ' ' && ev != '<' && ev != '>' && ev != 'X')
1056 return;
1057 curobj= op;
1058 typing= 0;
1059 magic= 0;
1062 void m_leave(int ev, object_t *op)
1063 /* About to leave this object. */
1065 if (ev != E_LEAVE) return;
1068 int within(unsigned *var, unsigned low, unsigned value, unsigned high)
1069 /* Only set *var to value if it looks reasonable. */
1071 if (low <= value && value <= high) {
1072 *var= value;
1073 return 1;
1074 } else
1075 return 0;
1078 int lwithin(unsigned long *var, unsigned long low, unsigned long value,
1079 unsigned long high)
1081 if (low <= value && value <= high) {
1082 *var= value;
1083 return 1;
1084 } else
1085 return 0;
1088 int nextdevice(object_t *op, int delta)
1089 /* Select the next or previous device from the device list. */
1091 dev_t rdev;
1093 if (offset != 0) return 0;
1094 if (dirty) event(E_WRITE, op);
1095 if (dirty) return 0;
1097 if (device >= 0) {
1098 (void) close(device);
1099 device= -1;
1101 recompute0();
1103 rdev= curdev->rdev;
1104 if (delta < 0) {
1106 curdev= curdev->prev;
1107 while (delta < -1 && major(curdev->rdev) == major(rdev)
1108 && curdev->rdev < rdev);
1109 } else {
1111 curdev= curdev->next;
1112 while (delta > 1 && major(curdev->rdev) == major(rdev)
1113 && curdev->rdev > rdev);
1115 return 1;
1118 void check_ind(struct part_entry *pe)
1119 /* If there are no other partitions then make this new one active. */
1121 struct part_entry *pe2;
1123 if (pe->sysind != NO_PART) return;
1125 for (pe2= table + 1; pe2 < table + 1 + NR_PARTITIONS; pe2++)
1126 if (pe2->sysind != NO_PART || pe2->bootind & ACTIVE_FLAG) break;
1128 if (pe2 == table + 1 + NR_PARTITIONS) pe->bootind= ACTIVE_FLAG;
1131 int check_existing(struct part_entry *pe)
1132 /* Check and if not ask if an existing partition may be modified. */
1134 static int expert= 0;
1135 int c;
1137 if (expert || pe == nil || !existing[pe - table]) return 1;
1139 stat_start(1);
1140 putstr("Do you wish to modify existing partitions? (y/n) ");
1141 fflush(stdout);
1142 while ((c= getchar()) != 'y' && c != 'n') {}
1143 putchr(c);
1144 stat_end(3);
1145 return (expert= (c == 'y'));
1148 void m_modify(int ev, object_t *op)
1149 /* Increment, decrement, set, or toggle the value of an object, using
1150 * arithmetic tricks the author doesn't understand either.
1153 object_t *op2;
1154 struct part_entry *pe= op->entry;
1155 int mul, delta;
1156 unsigned level= 1;
1157 unsigned long surplus;
1158 int radix= op->type == O_TYPHEX ? 0x10 : 10;
1159 unsigned long t;
1161 if (device < 0 && op->type != O_DEV) return;
1163 switch (ev) {
1164 case '-':
1165 mul= radix; delta= -1; typing= 0;
1166 break;
1167 case '+':
1168 mul= radix; delta= 1; typing= 0;
1169 break;
1170 case '\b':
1171 if (!typing) return;
1172 mul= 1; delta= 0;
1173 break;
1174 case '\r':
1175 typing= 0;
1176 return;
1177 default:
1178 if ('0' <= ev && ev <= '9')
1179 delta= ev - '0';
1180 else
1181 if (radix == 0x10 && 'a' <= ev && ev <= 'f')
1182 delta= ev - 'a' + 10;
1183 else
1184 if (radix == 0x10 && 'A' <= ev && ev <= 'F')
1185 delta= ev - 'A' + 10;
1186 else
1187 return;
1189 mul= typing ? radix*radix : 0;
1190 typing= 1;
1192 magic= 0;
1194 if (!check_existing(pe)) return;
1196 switch (op->type) {
1197 case O_DEV:
1198 if (ev != '-' && ev != '+') return;
1199 if (!nextdevice(op, delta)) return;
1200 break;
1201 case O_CYL:
1202 if (!within(&cylinders, 1,
1203 cylinders * mul / radix + delta, 1024)) return;
1204 recompute0();
1205 break;
1206 case O_HEAD:
1207 if (!within(&heads, 1, heads * mul / radix + delta, 255))
1208 return;
1209 recompute0();
1210 break;
1211 case O_SEC:
1212 if (!within(&sectors, 1, sectors * mul / radix + delta, 63))
1213 return;
1214 recompute0();
1215 break;
1216 case O_NUM:
1217 if (ev != '-' && ev != '+') return;
1218 for (op2= world; op2 != nil; op2= op2->next) {
1219 if (op2->type == O_NUM && ev == '+')
1220 op2->entry->bootind= 0;
1222 op->entry->bootind= ev == '+' ? ACTIVE_FLAG : 0;
1223 break;
1224 case O_TYPHEX:
1225 check_ind(pe);
1226 pe->sysind= pe->sysind * mul / radix + delta;
1227 break;
1228 case O_TYPTXT:
1229 if (ev != '-' && ev != '+') return;
1230 check_ind(pe);
1231 pe->sysind= round_sysind(pe->sysind, delta);
1232 break;
1233 case O_SCYL:
1234 level= heads;
1235 case O_SHEAD:
1236 level*= sectors;
1237 case O_SSEC:
1238 if (op->type != O_SCYL && ev != '-' && ev != '+') return;
1239 case O_BASE:
1240 if (pe->sysind == NO_PART) memset(pe, 0, sizeof(*pe));
1241 t= pe->lowsec;
1242 surplus= t % level;
1243 if (!lwithin(&t, 0L,
1244 (t / level * mul / radix + delta) * level + surplus,
1245 MAXSIZE)) return;
1246 if (howend == LAST || op->type != O_BASE)
1247 pe->size-= t - pe->lowsec;
1248 pe->lowsec= t;
1249 check_ind(pe);
1250 if (pe->sysind == NO_PART) pe->sysind= MINIX_PART;
1251 break;
1252 case O_LCYL:
1253 level= heads;
1254 case O_LHEAD:
1255 level*= sectors;
1256 case O_LSEC:
1257 if (op->type != O_LCYL && ev != '-' && ev != '+') return;
1259 if (pe->sysind == NO_PART) memset(pe, 0, sizeof(*pe));
1260 t= pe->lowsec + pe->size - 1 + level;
1261 surplus= t % level - mul / radix * level;
1262 if (!lwithin(&t, 0L,
1263 (t / level * mul / radix + delta) * level + surplus,
1264 MAXSIZE)) return;
1265 pe->size= t - pe->lowsec + 1;
1266 check_ind(pe);
1267 if (pe->sysind == NO_PART) pe->sysind= MINIX_PART;
1268 break;
1269 case O_KB:
1270 level= 2;
1271 if (mul == 0) pe->size= 0; /* new value, no surplus */
1272 case O_SIZE:
1273 if (pe->sysind == NO_PART) {
1274 if (op->type == O_KB || howend == SIZE) {
1275 /* First let loose magic to set the base. */
1276 event('m', op);
1277 magic= 0;
1278 pe->size= 0;
1279 event(ev, op);
1280 return;
1282 memset(pe, 0, sizeof(*pe));
1284 t= (op->type == O_KB || howend == SIZE) ? pe->size
1285 : pe->lowsec + pe->size - 1;
1286 surplus= t % level;
1287 if (!lwithin(&t, 0L,
1288 (t / level * mul / radix + delta) * level + surplus,
1289 MAXSIZE)) return;
1290 pe->size= (op->type == O_KB || howend == SIZE) ? t :
1291 t - pe->lowsec + 1;
1292 check_ind(pe);
1293 if (pe->sysind == NO_PART) pe->sysind= MINIX_PART;
1294 break;
1295 default:
1296 return;
1299 /* The order among the entries may have changed. */
1300 sort();
1301 dirty= 1;
1304 unsigned long spell[3 + 4 * (1+NR_PARTITIONS)];
1305 int nspells;
1306 objtype_t touching;
1308 void newspell(unsigned long charm)
1309 /* Add a new spell, descending order for the base, ascending for the size. */
1311 int i, j;
1313 if (charm - table[0].lowsec > table[0].size) return;
1315 for (i= 0; i < nspells; i++) {
1316 if (charm == spell[i]) return; /* duplicate */
1318 if (touching == O_BASE) {
1319 if (charm == table[0].lowsec + table[0].size) return;
1320 if ((spell[0] - charm) < (spell[0] - spell[i])) break;
1321 } else {
1322 if (charm == table[0].lowsec) return;
1323 if ((charm - spell[0]) < (spell[i] - spell[0])) break;
1326 for (j= ++nspells; j > i; j--) spell[j]= spell[j-1];
1327 spell[i]= charm;
1330 void m_magic(int ev, object_t *op)
1331 /* Apply magic onto a base or size number. */
1333 struct part_entry *pe= op->entry, *pe2;
1334 int rough= (offset != 0 && extbase == 0);
1336 if (ev != 'm' || device < 0) return;
1337 typing= 0;
1339 if (!check_existing(pe)) return;
1341 if (magic == 0) {
1342 /* See what magic we can let loose on this value. */
1343 nspells= 1;
1345 /* First spell, the current value. */
1346 switch (op->type) {
1347 case O_SCYL:
1348 case O_SHEAD: /* Start of partition. */
1349 case O_SSEC:
1350 case O_BASE:
1351 touching= O_BASE;
1352 spell[0]= pe->lowsec;
1353 break;
1354 case O_LCYL:
1355 case O_LHEAD:
1356 case O_LSEC: /* End of partition. */
1357 case O_KB:
1358 case O_SIZE:
1359 touching= O_SIZE;
1360 spell[0]= pe->lowsec + pe->size;
1361 break;
1362 default:
1363 return;
1365 if (pe->sysind == NO_PART) {
1366 memset(pe, 0, sizeof(*pe));
1367 check_ind(pe);
1368 pe->sysind= MINIX_PART;
1369 spell[0]= 0;
1370 if (touching == O_SIZE) {
1371 /* First let loose magic on the base. */
1372 object_t *op2;
1374 for (op2= world; op2 != nil; op2= op2->next) {
1375 if (op2->row == op->row &&
1376 op2->type == O_BASE) {
1377 event('m', op2);
1380 magic= 0;
1381 event('m', op);
1382 return;
1385 /* Avoid the first sector on the device. */
1386 if (spell[0] == table[0].lowsec) newspell(spell[0] + 1);
1388 /* Further interesting values are the the bases of other
1389 * partitions or their ends.
1391 for (pe2= table; pe2 < table + 1 + NR_PARTITIONS; pe2++) {
1392 if (pe2 == pe || pe2->sysind == NO_PART) continue;
1393 if (pe2->lowsec == table[0].lowsec)
1394 newspell(table[0].lowsec + 1);
1395 else
1396 newspell(pe2->lowsec);
1397 newspell(pe2->lowsec + pe2->size);
1398 if (touching == O_BASE && howend == SIZE) {
1399 newspell(pe2->lowsec - pe->size);
1400 newspell(pe2->lowsec + pe2->size - pe->size);
1402 if (pe2->lowsec % sectors != 0) rough= 1;
1404 /* Present values rounded up to the next cylinder unless
1405 * the table is already a mess. Use "start + 1 track" instead
1406 * of "start + 1 cylinder". Also add the end of the last
1407 * cylinder.
1409 if (!rough) {
1410 unsigned long n= spell[0];
1411 if (n == table[0].lowsec) n++;
1412 n= (n + sectors - 1) / sectors * sectors;
1413 if (n != table[0].lowsec + sectors)
1414 n= (n + secpcyl - 1) / secpcyl * secpcyl;
1415 newspell(n);
1416 if (touching == O_SIZE)
1417 newspell(table[0].size / secpcyl * secpcyl);
1420 /* Magic has been applied, a spell needs to be chosen. */
1422 if (++magic == nspells) magic= 0;
1424 if (touching == O_BASE) {
1425 if (howend == LAST) pe->size-= spell[magic] - pe->lowsec;
1426 pe->lowsec= spell[magic];
1427 } else
1428 pe->size= spell[magic] - pe->lowsec;
1430 /* The order among the entries may have changed. */
1431 sort();
1432 dirty= 1;
1435 typedef struct diving {
1436 struct diving *up;
1437 struct part_entry old0;
1438 char *oldsubname;
1439 parttype_t oldparttype;
1440 unsigned long oldoffset;
1441 unsigned long oldextbase;
1442 } diving_t;
1444 diving_t *diving= nil;
1446 void m_in(int ev, object_t *op)
1447 /* Go down into a primary or extended partition. */
1449 diving_t *newdiv;
1450 struct part_entry *pe= op->entry, ext;
1451 int n;
1453 if (ev != '>' || device < 0 || pe == nil || pe == &table[0]
1454 || (!(pe->sysind == MINIX_PART && offset == 0)
1455 && !ext_part(pe->sysind))
1456 || pe->size == 0) return;
1458 ext= *pe;
1459 if (extbase != 0) ext.size= extbase + extsize - ext.lowsec;
1461 if (dirty) event(E_WRITE, op);
1462 if (dirty) return;
1463 if (device >= 0) { close(device); device= -1; }
1465 newdiv= alloc(sizeof(*newdiv));
1466 newdiv->old0= table[0];
1467 newdiv->oldsubname= curdev->subname;
1468 newdiv->oldparttype= curdev->parttype;
1469 newdiv->oldoffset= offset;
1470 newdiv->oldextbase= extbase;
1471 newdiv->up= diving;
1472 diving= newdiv;
1474 table[0]= ext;
1476 n= strlen(diving->oldsubname);
1477 curdev->subname= alloc((n + 3) * sizeof(curdev->subname[0]));
1478 strcpy(curdev->subname, diving->oldsubname);
1479 curdev->subname[n++]= ':';
1480 curdev->subname[n++]= '0' + (pe - table - 1);
1481 curdev->subname[n]= 0;
1483 curdev->parttype= curdev->parttype == PRIMARY ? SUBPART : DUNNO;
1484 offset= ext.lowsec;
1485 if (ext_part(ext.sysind) && extbase == 0) {
1486 extbase= ext.lowsec;
1487 extsize= ext.size;
1488 curdev->parttype= DUNNO;
1491 submerged= 1;
1492 event('r', op);
1495 void m_out(int ev, object_t *op)
1496 /* Go up from an extended or subpartition table to its enclosing. */
1498 diving_t *olddiv;
1500 if (ev != '<' || diving == nil) return;
1502 if (dirty) event(E_WRITE, op);
1503 if (dirty) return;
1504 if (device >= 0) { close(device); device= -1; }
1506 olddiv= diving;
1507 diving= olddiv->up;
1509 table[0]= olddiv->old0;
1511 free(curdev->subname);
1512 curdev->subname= olddiv->oldsubname;
1514 curdev->parttype= olddiv->oldparttype;
1515 offset= olddiv->oldoffset;
1516 extbase= olddiv->oldextbase;
1518 free(olddiv);
1520 event('r', op);
1521 if (diving == nil) submerged= 0; /* We surfaced. */
1524 void installboot(unsigned char *bootblock, char *masterboot)
1525 /* Install code from a master bootstrap into a boot block. */
1527 FILE *mfp;
1528 unsigned char buf[SECTOR_SIZE];
1529 int n;
1530 char *err;
1532 if ((mfp= fopen(masterboot, "r")) == nil) {
1533 err= strerror(errno);
1534 goto m_err;
1537 n= fread(buf, sizeof(char), SECTOR_SIZE, mfp);
1538 if (ferror(mfp)) {
1539 err= strerror(errno);
1540 fclose(mfp);
1541 goto m_err;
1543 else if (n < 256) {
1544 err= "Is probably not a boot sector, too small";
1545 fclose(mfp);
1546 goto m_err;
1548 else if (n < SECTOR_SIZE && n > PART_TABLE_OFF) {
1549 /* if only code, it cannot override partition table */
1550 err= "Does not fit in a boot sector";
1551 fclose(mfp);
1552 goto m_err;
1554 else if (n == SECTOR_SIZE) {
1555 if (buf[510] != 0x55 || buf[511] != 0xaa) {
1556 err= "Is not a boot sector (bad magic)";
1557 fclose(mfp);
1558 goto m_err;
1560 n = PART_TABLE_OFF;
1563 if (n > PART_TABLE_OFF) {
1564 err= "Does not fit in a boot sector";
1565 fclose(mfp);
1566 goto m_err;
1569 memcpy(bootblock, buf, n);
1570 fclose(mfp);
1572 /* Bootstrap installed. */
1573 return;
1575 m_err:
1576 stat_start(1);
1577 printf("%s: %s", masterboot, err);
1578 stat_end(5);
1581 ssize_t boot_readwrite(int rw)
1582 /* Read (0) or write (1) the boot sector. */
1584 u64_t off64 = mul64u(offset, SECTOR_SIZE);
1585 int r;
1587 #if __minix_vmd
1588 /* Minix-vmd has a 64 bit seek. */
1589 if (fcntl(device, F_SEEK, off64) < 0) return -1;
1590 #else
1591 /* Minix has to gross things with the partition base. */
1592 struct partition geom0, geom_seek;
1594 if (offset >= (LONG_MAX / SECTOR_SIZE - 1)) {
1595 /* Move partition base. */
1596 if (ioctl(device, DIOCGETP, &geom0) < 0) return -1;
1597 geom_seek.base = add64(geom0.base, off64);
1598 geom_seek.size = cvu64(cmp64(add64u(off64, SECTOR_SIZE),
1599 geom0.size) <= 0 ? _STATIC_BLOCK_SIZE : 0);
1600 sync();
1601 if (ioctl(device, DIOCSETP, &geom_seek) < 0) return -1;
1602 if (lseek(device, (off_t) 0, SEEK_SET) == -1) return -1;
1603 } else {
1604 /* Can reach this point normally. */
1605 if (lseek(device, (off_t) offset * SECTOR_SIZE, SEEK_SET) == -1)
1606 return -1;
1608 #endif
1610 switch (rw) {
1611 case 0: r= read(device, bootblock, SECTOR_SIZE); break;
1612 case 1: r= write(device, bootblock, SECTOR_SIZE); break;
1615 #if !__minix_vmd
1616 if (offset >= (LONG_MAX / SECTOR_SIZE - 1)) {
1617 /* Restore partition base and size. */
1618 sync();
1619 if (ioctl(device, DIOCSETP, &geom0) < 0) return -1;
1621 #endif
1622 return r;
1625 void m_read(int ev, object_t *op)
1626 /* Read the partition table from the current device. */
1628 int i, mode, n;
1629 struct part_entry *pe;
1631 if (ev != 'r' || device >= 0) return;
1633 /* Open() may cause kernel messages: */
1634 stat_start(0);
1635 fflush(stdout);
1637 if (((device= open(curdev->name, mode= O_RDWR, 0666)) < 0
1638 && (errno != EACCES
1639 || (device= open(curdev->name, mode= O_RDONLY)) < 0))
1641 stat_start(1);
1642 printf("%s: %s", curdev->name, strerror(errno));
1643 stat_end(5);
1644 if (device >= 0) { close(device); device= -1; }
1645 return;
1648 /* Assume up to five lines of kernel messages. */
1649 statusrow+= 5-1;
1650 stat_end(5);
1652 if (mode == O_RDONLY) {
1653 stat_start(1);
1654 printf("%s: Readonly", curdev->name);
1655 stat_end(5);
1657 memset(bootblock, 0, sizeof(bootblock));
1659 n= boot_readwrite(0);
1661 if (n <= 0) stat_start(1);
1662 if (n < 0) {
1663 printf("%s: %s", curdev->name, strerror(errno));
1664 close(device);
1665 device= -1;
1666 } else
1667 if (n < SECTOR_SIZE) printf("%s: Unexpected EOF", curdev->subname);
1668 if (n <= 0) stat_end(5);
1670 if (n < SECTOR_SIZE) n= SECTOR_SIZE;
1672 memcpy(table+1, bootblock+PART_TABLE_OFF,
1673 NR_PARTITIONS * sizeof(table[1]));
1674 for (i= 1; i <= NR_PARTITIONS; i++) {
1675 if ((table[i].bootind & ~ACTIVE_FLAG) != 0) break;
1677 if (i <= NR_PARTITIONS || bootblock[510] != 0x55
1678 || bootblock[511] != 0xAA) {
1679 /* Invalid boot block, install bootstrap, wipe partition table.
1681 memset(bootblock, 0, sizeof(bootblock));
1682 installboot(bootblock, MASTERBOOT);
1683 memset(table+1, 0, NR_PARTITIONS * sizeof(table[1]));
1684 stat_start(1);
1685 printf("%s: Invalid partition table (reset)", curdev->subname);
1686 stat_end(5);
1689 /* Fix an extended partition table up to something mere mortals can
1690 * understand. Record already defined partitions.
1692 for (i= 1; i <= NR_PARTITIONS; i++) {
1693 pe= &table[i];
1694 if (extbase != 0 && pe->sysind != NO_PART)
1695 pe->lowsec+= ext_part(pe->sysind) ? extbase : offset;
1696 existing[i]= pe->sysind != NO_PART;
1698 geometry();
1699 dirty= 0;
1701 /* Warn about grave dangers ahead. */
1702 if (extbase != 0) {
1703 stat_start(1);
1704 printf("Warning: You are in an extended partition.");
1705 stat_end(5);
1709 void m_write(int ev, object_t *op)
1710 /* Write the partition table back if modified. */
1712 int c;
1713 struct part_entry new_table[NR_PARTITIONS], *pe;
1715 if (ev != 'w' && ev != E_WRITE) return;
1716 if (device < 0) { dirty= 0; return; }
1717 if (!dirty) {
1718 if (ev == 'w') {
1719 stat_start(1);
1720 printf("%s is not changed, or has already been written",
1721 curdev->subname);
1722 stat_end(2);
1724 return;
1727 if (bootblock[510] != 0x55 || bootblock[511] != 0xAA) {
1728 /* Invalid boot block, warn user. */
1729 stat_start(1);
1730 printf("Warning: About to write a new table on %s",
1731 curdev->subname);
1732 stat_end(5);
1734 if (extbase != 0) {
1735 /* Will this stop the luser? Probably not... */
1736 stat_start(1);
1737 printf("You have changed an extended partition. Bad Idea.");
1738 stat_end(5);
1740 stat_start(1);
1741 putstr("Save partition table? (y/n) ");
1742 fflush(stdout);
1744 while ((c= getchar()) != 'y' && c != 'n' && c != ctrl('?')) {}
1746 if (c == ctrl('?')) putstr("DEL"); else putchr(c);
1747 stat_end(5);
1748 if (c == 'n' && ev == E_WRITE) dirty= 0;
1749 if (c != 'y') return;
1751 memcpy(new_table, table+1, NR_PARTITIONS * sizeof(table[1]));
1752 for (pe= new_table; pe < new_table + NR_PARTITIONS; pe++) {
1753 if (pe->sysind == NO_PART) {
1754 memset(pe, 0, sizeof(*pe));
1755 } else {
1756 abs2dos(&pe->start_head, pe->lowsec);
1757 abs2dos(&pe->last_head, pe->lowsec + pe->size - 1);
1759 /* Fear and loathing time: */
1760 if (extbase != 0)
1761 pe->lowsec-= ext_part(pe->sysind)
1762 ? extbase : offset;
1765 memcpy(bootblock+PART_TABLE_OFF, new_table, sizeof(new_table));
1766 bootblock[510]= 0x55;
1767 bootblock[511]= 0xAA;
1769 if (boot_readwrite(1) < 0) {
1770 stat_start(1);
1771 printf("%s: %s", curdev->name, strerror(errno));
1772 stat_end(5);
1773 return;
1775 dirty= 0;
1778 void m_shell(int ev, object_t *op)
1779 /* Shell escape, to do calculations for instance. */
1781 int r, pid, status;
1782 void (*sigint)(int), (*sigquit)(int), (*sigterm)(int);
1784 if (ev != 's') return;
1786 reset_tty();
1787 fflush(stdout);
1789 switch (pid= fork()) {
1790 case -1:
1791 stat_start(1);
1792 printf("can't fork: %s\n", strerror(errno));
1793 stat_end(3);
1794 break;
1795 case 0:
1796 if (device >= 0) (void) close(device);
1797 execl("/bin/sh", "sh", (char *) nil);
1798 r= errno;
1799 stat_start(1);
1800 printf("/bin/sh: %s\n", strerror(errno));
1801 stat_end(3);
1802 exit(127);
1804 sigint= signal(SIGINT, SIG_IGN);
1805 sigquit= signal(SIGQUIT, SIG_IGN);
1806 sigterm= signal(SIGTERM, SIG_IGN);
1807 while (pid >= 0 && (r= wait(&status)) >= 0 && r != pid) {}
1808 (void) signal(SIGINT, sigint);
1809 (void) signal(SIGQUIT, sigquit);
1810 (void) signal(SIGTERM, sigterm);
1811 tty_raw();
1812 if (pid < 0)
1814 else
1815 if (WIFEXITED(status) && WEXITSTATUS(status) == 127)
1816 stat_start(0); /* Match the stat_start in the child. */
1817 else
1818 event(ctrl('L'), op);
1821 void m_dump(int ev, object_t *op)
1822 /* Raw dump of the partition table. */
1824 struct part_entry table[NR_PARTITIONS], *pe;
1825 int i;
1826 unsigned chs[3];
1828 if (ev != 'p' || device < 0) return;
1830 memcpy(table, bootblock+PART_TABLE_OFF,
1831 NR_PARTITIONS * sizeof(table[0]));
1832 for (i= 0; i < NR_PARTITIONS; i++) {
1833 pe= &table[i];
1834 stat_start(0);
1835 dos2chs(&pe->start_head, chs);
1836 printf("%2d%c %02X%15d%5d%4d",
1837 i+1,
1838 pe->bootind & ACTIVE_FLAG ? '*' : ' ',
1839 pe->sysind,
1840 chs[0], chs[1], chs[2]);
1841 dos2chs(&pe->last_head, chs);
1842 printf("%6d%5d%4d%10lu%10ld%9lu",
1843 chs[0], chs[1], chs[2],
1844 pe->lowsec,
1845 howend == SIZE ? pe->size : pe->size + pe->lowsec - 1,
1846 pe->size / 2);
1847 stat_end(10);
1849 stat_start(0);
1850 printf("(Raw dump of the original %.40s table)",
1851 curdev->subname);
1852 stat_end(10);
1855 int quitting= 0;
1857 void m_quit(int ev, object_t *op)
1858 /* Write the partition table if modified and exit. */
1860 if (ev != 'q' && ev != 'x') return;
1862 quitting= 1;
1864 if (dirty) event(E_WRITE, op);
1865 if (dirty) quitting= 0;
1868 void m_help(int ev, object_t *op)
1869 /* For people without a clue; let's hope they can find the '?' key. */
1871 static struct help {
1872 char *keys;
1873 char *what;
1874 } help[]= {
1875 { "? !", "This help / more advice!" },
1876 { "+ - (= _ PgUp PgDn)","Select/increment/decrement/make active" },
1877 { "0-9 (a-f)", "Enter value" },
1878 { "hjkl (arrow keys)", "Move around" },
1879 { "CTRL-K CTRL-J", "Move entry up/down" },
1880 { "CTRL-L", "Redraw screen" },
1881 { ">", "Start a subpartition table" },
1882 { "<", "Back to the primary partition table" },
1883 { "m", "Cycle through magic values" },
1884 { "spacebar", "Show \"Size\" or \"Last\"" },
1885 { "r w", "Read/write partition table" },
1886 { "p s q x", "Raw dump / Shell escape / Quit / Exit" },
1887 { "y n DEL", "Answer \"yes\", \"no\", \"cancel\"" },
1889 static char *advice[] = {
1890 "* Choose a disk with '+' and '-', then hit 'r'.",
1891 "* To change any value: Move to it and use '+', '-' or type the desired value.",
1892 "* To make a new partition: Move over to the Size or Kb field of an unused",
1893 " partition and type the size. Hit the 'm' key to pad the partition out to",
1894 " a cylinder boundary. Hit 'm' again to pad it out to the end of the disk.",
1895 " You can hit 'm' more than once on a base or size field to see several",
1896 " interesting values go by. Note: Other Operating Systems can be picky about",
1897 " partitions that are not padded to cylinder boundaries. Look for highlighted",
1898 " head or sector numbers.",
1899 "* To reuse a partition: Change the type to MINIX.",
1900 "* To delete a partition: Type a zero in the hex Type field.",
1901 "* To make a partition active: Type '+' in the Num field.",
1902 "* To study the list of keys: Type '?'.",
1905 if (ev == '?') {
1906 struct help *hp;
1908 for (hp= help; hp < arraylimit(help); hp++) {
1909 stat_start(0);
1910 printf("%-25s - %s", hp->keys, hp->what);
1911 stat_end(0);
1913 stat_start(0);
1914 putstr("Things like ");
1915 putstr(t_so); putstr("this"); putstr(t_se);
1916 putstr(" must be checked, but ");
1917 putstr(t_md); putstr("this"); putstr(t_me);
1918 putstr(" is not really a problem");
1919 stat_end(0);
1920 } else
1921 if (ev == '!') {
1922 char **ap;
1924 for (ap= advice; ap < arraylimit(advice); ap++) {
1925 stat_start(0);
1926 putstr(*ap);
1927 stat_end(0);
1932 void event(int ev, object_t *op)
1933 /* Simply call all modifiers for an event, each one knows when to act. */
1935 m_help(ev, op);
1936 m_redraw(ev, op);
1937 m_toggle(ev, op);
1938 m_orientation(ev, op);
1939 m_move(ev, op);
1940 m_updown(ev, op);
1941 m_enter(ev, op);
1942 m_leave(ev, op);
1943 m_modify(ev, op);
1944 m_magic(ev, op);
1945 m_in(ev, op);
1946 m_out(ev, op);
1947 m_read(ev, op);
1948 m_write(ev, op);
1949 m_shell(ev, op);
1950 m_dump(ev, op);
1951 m_quit(ev, op);
1954 int keypress(void)
1955 /* Get a single keypress. Translate compound keypresses (arrow keys) to
1956 * their simpler equivalents.
1959 char ch;
1960 int c;
1961 int esc= 0;
1963 set_cursor(curobj->row, curobj->col);
1964 fflush(stdout);
1966 do {
1967 if (read(0, &ch, sizeof(ch)) < 0) fatal("stdin");
1968 c= (unsigned char) ch;
1969 switch (esc) {
1970 case 0:
1971 switch (c) {
1972 case ctrl('['): esc= 1; break;
1973 case '_': c= '-'; break;
1974 case '=': c= '+'; break;
1976 break;
1977 case 1:
1978 esc= c == '[' ? 2 : 0;
1979 break;
1980 case 2:
1981 switch (c) {
1982 case 'D': c= 'h'; break;
1983 case 'B': c= 'j'; break;
1984 case 'A': c= 'k'; break;
1985 case 'C': c= 'l'; break;
1986 case 'H': c= 'H'; break;
1987 case 'U':
1988 case 'S': c= '-'; break;
1989 case 'V':
1990 case 'T': c= '+'; break;
1992 /*FALL THROUGH*/
1993 default:
1994 esc= 0;
1996 } while (esc > 0);
1998 switch (c) {
1999 case ctrl('B'): c= 'h'; break;
2000 case ctrl('N'): c= 'j'; break;
2001 case ctrl('P'): c= 'k'; break;
2002 case ctrl('F'): c= 'l'; break;
2005 return c;
2008 void mainloop(void)
2009 /* Get keypress, handle event, display results, reset screen, ad infinitum. */
2011 int key;
2013 while (!quitting) {
2014 stat_reset();
2016 key= keypress();
2018 event(key, curobj);
2020 display();
2024 int main(int argc, char **argv)
2026 object_t *op;
2027 int i, r, key;
2028 struct part_entry *pe;
2030 /* Define a few objects to show on the screen. First text: */
2031 op= newobject(O_INFO, 0, 0, 2, 19);
2032 op= newobject(O_TEXT, 0, 0, 22, 13); op->text= "----first----";
2033 op= newobject(O_TEXT, 0, 0, 37, 13); op->text= "--geom/last--";
2034 op= newobject(O_TEXT, 0, 0, 52, 18); op->text= "------sectors-----";
2035 op= newobject(O_TEXT, 0, 1, 4, 6); op->text= "Device";
2036 op= newobject(O_TEXT, 0, 1, 23, 12); op->text= "Cyl Head Sec";
2037 op= newobject(O_TEXT, 0, 1, 38, 12); op->text= "Cyl Head Sec";
2038 op= newobject(O_TEXT, 0, 1, 56, 4); op->text= "Base";
2039 op= newobject(O_TEXT, 0, 1, 66, 4); op->text= size_last;
2040 op= newobject(O_TEXT, 0, 1, 78, 2); op->text= "Kb";
2041 op= newobject(O_TEXT, 0, 4, 0, 15); op->text= "Num Sort Type";
2043 /* The device is the current object: */
2044 curobj= newobject(O_DEV, OF_MOD, 2, 4, 15);
2045 op= newobject(O_SUB, 0, 3, 4, 15);
2047 /* Geometry: */
2048 op= newobject(O_CYL, OF_MOD, 2, 40, 5); op->entry= &table[0];
2049 op= newobject(O_HEAD, OF_MOD, 2, 45, 3); op->entry= &table[0];
2050 op= newobject(O_SEC, OF_MOD, 2, 49, 2); op->entry= &table[0];
2052 /* Objects for the device: */
2053 op= newobject(O_SCYL, 0, 3, 25, 5); op->entry= &table[0];
2054 op= newobject(O_SHEAD, 0, 3, 30, 3); op->entry= &table[0];
2055 op= newobject(O_SSEC, 0, 3, 34, 2); op->entry= &table[0];
2056 op= newobject(O_LCYL, 0, 3, 40, 5); op->entry= &table[0];
2057 op= newobject(O_LHEAD, 0, 3, 45, 3); op->entry= &table[0];
2058 op= newobject(O_LSEC, 0, 3, 49, 2); op->entry= &table[0];
2059 op= newobject(O_BASE, 0, 3, 59, 9); op->entry= &table[0];
2060 op= newobject(O_SIZE, 0, 3, 69, 9); op->entry= &table[0];
2061 op= newobject(O_KB, 0, 3, 79, 9); op->entry= &table[0];
2063 /* Objects for each partition: */
2064 for (r= 5, pe= table+1; pe <= table+NR_PARTITIONS; r++, pe++) {
2065 op= newobject(O_NUM, OF_MOD, r, 1, 2); op->entry= pe;
2066 op= newobject(O_SORT, 0, r, 5, 2); op->entry= pe;
2067 op= newobject(O_TYPHEX, OF_MOD, r, 10, 2); op->entry= pe;
2068 op= newobject(O_TYPTXT, OF_MOD, r, 12, 9); op->entry= pe;
2069 op= newobject(O_SCYL, OF_MOD, r, 25, 5); op->entry= pe;
2070 op= newobject(O_SHEAD, OF_MOD, r, 30, 3); op->entry= pe;
2071 op= newobject(O_SSEC, OF_MOD, r, 34, 2); op->entry= pe;
2072 op= newobject(O_LCYL, OF_MOD, r, 40, 5); op->entry= pe;
2073 op= newobject(O_LHEAD, OF_MOD, r, 45, 3); op->entry= pe;
2074 op= newobject(O_LSEC, OF_MOD, r, 49, 2); op->entry= pe;
2075 op= newobject(O_BASE, OF_MOD, r, 59, 9); op->entry= pe;
2076 op= newobject(O_SIZE, OF_MOD, r, 69, 9); op->entry= pe;
2077 op= newobject(O_KB, OF_MOD, r, 79, 9); op->entry= pe;
2080 for (i= 1; i < argc; i++) newdevice(argv[i], 0);
2082 if (firstdev == nil) {
2083 getdevices();
2084 key= ctrl('L');
2085 } else {
2086 key= 'r';
2089 if (firstdev != nil) {
2090 init_tty();
2091 clear_screen();
2092 event(key, curobj);
2093 display();
2094 mainloop();
2095 reset_tty();
2097 exit(0);