Remove building with NOCRYPTO option
[minix3.git] / minix / commands / part / part.c
blob13e6173fe8ce671d22064da398193258aad454f0
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 <machine/partition.h>
26 #include <termios.h>
28 /* True if a partition is an extended partition. */
29 #define ext_part(s) ((s) == 0x05 || (s) == 0x0F)
31 /* Minix master bootstrap code. */
32 static char MASTERBOOT[] = "/usr/mdec/mbr";
34 /* Template:
35 ----first---- --geom/last-- ------sectors-----
36 Device Cyl Head Sec Cyl Head Sec Base Size Kb
37 /dev/c0d0 977 5 17
38 /dev/c0d0:2 0 0 2 976 4 16 2 83043 41521
39 Num Sort Type
40 0* p0 81 MINIX 0 0 3 33 4 9 3 2880 1440
41 1 p1 81 MINIX 33 4 10 178 2 2 2883 12284 6142
42 2 p2 81 MINIX 178 2 3 976 4 16 15167 67878 33939
43 3 p3 00 None 0 0 0 0 0 -1 0 0 0
46 #define MAXSIZE 999999999L /* Will 1T be enough this year? */
47 #define SECTOR_SIZE 512
48 #define DEV_FD0 0x200 /* Device number of /dev/fd0 */
49 #define DEV_C0D0 0x300 /* Device number of /dev/c0d0 */
51 #define arraysize(a) (sizeof(a) / sizeof((a)[0]))
52 #define arraylimit(a) ((a) + arraysize(a))
54 void report(const char *label)
56 fprintf(stderr, "part: %s: %s\n", label, strerror(errno));
59 void fatal(const char *label)
61 report(label);
62 exit(1);
65 struct termios termios;
67 void save_ttyflags(void)
68 /* Save tty attributes for later restoration. */
70 if (tcgetattr(0, &termios) < 0) fatal("");
73 void restore_ttyflags(void)
74 /* Reset the tty flags to how we got 'em. */
76 if (tcsetattr(0, TCSANOW, &termios) < 0) fatal("");
79 void tty_raw(void)
80 /* Set the terminal to raw mode, no signals, no echoing. */
82 struct termios rawterm;
84 rawterm= termios;
85 rawterm.c_lflag &= ~(ICANON|ISIG|ECHO);
86 rawterm.c_iflag &= ~(ICRNL);
87 if (tcsetattr(0, TCSANOW, &rawterm) < 0) fatal("");
90 #define ctrl(c) ((c) == '?' ? '\177' : ((c) & '\37'))
92 char t_cd[16], t_cm[32], t_so[16], t_se[16], t_md[16], t_me[16];
93 int t_li, t_co;
94 #define STATUSROW 10
96 void init_tty(void)
97 /* Get terminal capabilities and set the tty to "editor" mode. */
99 char *term;
100 static char termbuf[1024];
101 char *tp;
103 if ((term= getenv("TERM")) == nil || tgetent(termbuf, term) != 1) {
104 fprintf(stderr, "part: Can't get terminal capabilities\n");
105 exit(1);
107 if (tgetstr("cd", (tp= t_cd, &tp)) == nil
108 || tgetstr("cm", (tp= t_cm, &tp)) == nil) {
109 fprintf(stderr, "part: This terminal is too dumb\n");
110 exit(1);
112 t_li= tgetnum("li");
113 t_co= tgetnum("co");
114 (void) tgetstr("so", (tp= t_so, &tp));
115 (void) tgetstr("se", (tp= t_se, &tp));
116 (void) tgetstr("md", (tp= t_md, &tp));
117 (void) tgetstr("me", (tp= t_me, &tp));
119 save_ttyflags();
120 tty_raw();
123 int putchr(int c)
125 return putchar(c);
128 void putstr(char *s)
130 int c;
132 while ((c= *s++) != 0) putchr(c);
135 void set_cursor(int row, int col)
137 tputs(tgoto(t_cm, col, row), 1, putchr);
140 int statusrow= STATUSROW;
141 int stat_ktl= 1;
142 int need_help= 1;
144 void stat_start(int serious)
145 /* Prepare for printing on a fresh status line, possibly highlighted. */
147 set_cursor(statusrow++, 0);
148 tputs(t_cd, 1, putchr);
149 if (serious) tputs(t_so, 1, putchr);
152 void stat_end(int ktl)
153 /* Closing bracket for stat_start. Sets "keystrokes to live" of message. */
155 tputs(t_se, 1, putchr);
156 stat_ktl= ktl;
157 need_help= 1;
160 void stat_reset(void)
161 /* Reset the statusline pointer and clear old messages if expired. */
163 if (stat_ktl > 0 && --stat_ktl == 0) {
164 statusrow= STATUSROW;
165 need_help= 1;
167 if (need_help && statusrow < (24-2)) {
168 if (statusrow > STATUSROW) stat_start(0);
169 stat_start(0);
170 putstr(
171 "Type '+' or '-' to change, 'r' to read, '?' for more help, 'q' to exit");
173 statusrow= STATUSROW;
174 need_help= 0;
177 void clear_screen(void)
179 set_cursor(0, 0);
180 tputs(t_cd, 1, putchr);
181 stat_ktl= 1;
182 stat_reset();
185 void reset_tty(void)
186 /* Reset the tty to cooked mode. */
188 restore_ttyflags();
189 set_cursor(statusrow, 0);
190 tputs(t_cd, 1, putchr);
193 void *alloc(size_t n)
195 void *m;
197 if ((m= malloc(n)) == nil) { reset_tty(); fatal(""); }
199 return m;
202 typedef enum parttype { DUNNO, SUBPART, PRIMARY, FLOPPY } parttype_t;
204 typedef struct device {
205 struct device *next, *prev; /* Circular dequeue. */
206 dev_t rdev; /* Device number (sorting only). */
207 char *name; /* E.g. /dev/c0d0 */
208 char *subname; /* E.g. /dev/c0d0:2 */
209 parttype_t parttype;
210 } device_t;
212 device_t *firstdev= nil, *curdev;
214 void newdevice(char *name, int scanning)
215 /* Add a device to the device list. If scanning is set then we are reading
216 * /dev, so insert the device in device number order and make /dev/c0d0 current.
219 device_t *new, *nextdev, *prevdev;
220 struct stat st;
222 st.st_rdev= 0;
223 if (scanning) {
224 if (stat(name, &st) < 0 || !S_ISBLK(st.st_mode)) return;
226 switch (major(st.st_rdev)) {
227 case 2:
228 /* Floppy */
229 if (minor(st.st_rdev) >= 4) return;
230 break;
231 case 3:
232 case 8:
233 case 10:
234 case 12:
235 case 16:
236 /* Disk controller */
237 if (minor(st.st_rdev) >= 0x80
238 || minor(st.st_rdev) % 5 != 0) return;
239 break;
240 default:
241 return;
243 /* Interesting device found. */
244 } else {
245 (void) stat(name, &st);
248 new= alloc(sizeof(*new));
249 new->rdev= st.st_rdev;
250 new->name= alloc((strlen(name) + 1) * sizeof(new->name[0]));
251 strcpy(new->name, name);
252 new->subname= new->name;
253 new->parttype= DUNNO;
254 if (major(st.st_rdev) == major(DEV_FD0) && minor(st.st_rdev) < 112) {
255 new->parttype= FLOPPY;
256 } else
257 if (st.st_rdev >= DEV_C0D0 && minor(st.st_rdev) < 128
258 && minor(st.st_rdev) % 5 == 0) {
259 new->parttype= PRIMARY;
262 if (firstdev == nil) {
263 firstdev= new;
264 new->next= new->prev= new;
265 curdev= firstdev;
266 return;
268 nextdev= firstdev;
269 while (new->rdev >= nextdev->rdev
270 && (nextdev= nextdev->next) != firstdev) {}
271 prevdev= nextdev->prev;
272 new->next= nextdev;
273 nextdev->prev= new;
274 new->prev= prevdev;
275 prevdev->next= new;
277 if (new->rdev < firstdev->rdev) firstdev= new;
278 if (new->rdev == DEV_C0D0) curdev= new;
279 if (curdev->rdev != DEV_C0D0) curdev= firstdev;
282 void getdevices(void)
283 /* Get all block devices from /dev that look interesting. */
285 DIR *d;
286 struct dirent *e;
287 char name[5 + NAME_MAX + 1];
289 if ((d= opendir("/dev")) == nil) fatal("/dev");
291 while ((e= readdir(d)) != nil) {
292 strcpy(name, "/dev/");
293 strcpy(name + 5, e->d_name);
294 newdevice(name, 1);
296 (void) closedir(d);
299 int dirty= 0;
300 unsigned char bootblock[SECTOR_SIZE];
301 struct part_entry table[1 + NR_PARTITIONS];
302 int existing[1 + NR_PARTITIONS];
303 unsigned long offset= 0, extbase= 0, extsize;
304 int submerged= 0;
305 char sort_index[1 + NR_PARTITIONS];
306 unsigned cylinders= 1, heads= 1, sectors= 1, secpcyl= 1;
307 unsigned alt_cyls= 1, alt_heads= 1, alt_secs= 1;
308 int precise= 0;
309 int device= -1;
311 unsigned long sortbase(struct part_entry *pe)
313 return pe->sysind == NO_PART ? -1 : pe->lowsec;
316 void sort(void)
317 /* Let the sort_index array show the order partitions are sorted in. */
319 int i, j;
320 int idx[1 + NR_PARTITIONS];
322 for (i= 1; i <= NR_PARTITIONS; i++) idx[i]= i;
324 for (i= 1; i <= NR_PARTITIONS; i++) {
325 for (j= 1; j <= NR_PARTITIONS-1; j++) {
326 int sj= idx[j], sj1= idx[j+1];
328 if (sortbase(&table[sj]) > sortbase(&table[sj1])) {
329 idx[j]= sj1;
330 idx[j+1]= sj;
334 for (i= 1; i <= NR_PARTITIONS; i++) sort_index[idx[i]]= i;
337 void dos2chs(unsigned char *dos, unsigned *chs)
338 /* Extract cylinder, head and sector from the three bytes DOS uses to address
339 * a sector. Note that bits 8 & 9 of the cylinder number come from bit 6 & 7
340 * of the sector byte. The sector number is rebased to count from 0.
343 chs[0]= ((dos[1] & 0xC0) << 2) | dos[2];
344 chs[1]= dos[0];
345 chs[2]= (dos[1] & 0x3F) - 1;
348 void abs2dos(unsigned char *dos, unsigned long pos)
349 /* Translate a sector offset to three DOS bytes. */
351 unsigned h, c, s;
353 c= pos / secpcyl;
354 h= (pos % secpcyl) / sectors;
355 s= pos % sectors + 1;
357 dos[0]= h;
358 dos[1]= s | ((c >> 2) & 0xC0);
359 dos[2]= c & 0xFF;
362 void recompute0(void)
363 /* Recompute the partition size for the device after a geometry change. */
365 if (device < 0) {
366 cylinders= heads= sectors= 1;
367 memset(table, 0, sizeof(table));
368 } else
369 if (!precise && offset == 0) {
370 table[0].lowsec= 0;
371 table[0].size= (unsigned long) cylinders * heads * sectors;
373 table[0].sysind= device < 0 ? NO_PART : MINIX_PART;
374 secpcyl= heads * sectors;
377 void guess_geometry(void)
378 /* With a bit of work one can deduce the disk geometry from the partition
379 * table. This may be necessary if the driver gets it wrong. (If partition
380 * tables didn't have C/H/S numbers we would not care at all...)
383 int i, n;
384 struct part_entry *pe;
385 unsigned chs[3];
386 unsigned long sec;
387 unsigned h, s;
388 unsigned char HS[256][8]; /* Bit map off all possible H/S */
390 alt_cyls= alt_heads= alt_secs= 0;
392 /* Initially all possible H/S combinations are possible. HS[h][0]
393 * bit 0 is used to rule out a head value.
395 for (h= 1; h <= 255; h++) {
396 for (s= 0; s < 8; s++) HS[h][s]= 0xFF;
399 for (i= 0; i < 2*NR_PARTITIONS; i++) {
400 pe= &(table+1)[i >> 1];
401 if (pe->sysind == NO_PART) continue;
403 /* Get the end or start sector numbers (in that order). */
404 if ((i & 1) == 0) {
405 dos2chs(&pe->last_head, chs);
406 sec= pe->lowsec + pe->size - 1;
407 } else {
408 dos2chs(&pe->start_head, chs);
409 sec= pe->lowsec;
412 if (chs[0] >= alt_cyls) alt_cyls= chs[0]+1;
414 /* Which H/S combinations can be ruled out? */
415 for (h= 1; h <= 255; h++) {
416 if (HS[h][0] == 0) continue;
417 n = 0;
418 for (s= 1; s <= 63; s++) {
419 if ((chs[0] * h + chs[1]) * s + chs[2] != sec) {
420 HS[h][s/8] &= ~(1 << (s%8));
422 if (HS[h][s/8] & (1 << (s%8))) n++;
424 if (n == 0) HS[h][0]= 0;
428 /* See if only one remains. */
429 i= 0;
430 for (h= 1; h <= 255; h++) {
431 if (HS[h][0] == 0) continue;
432 for (s= 1; s <= 63; s++) {
433 if (HS[h][s/8] & (1 << (s%8))) {
434 i++;
435 alt_heads= h;
436 alt_secs= s;
441 /* Forget it if more than one choice... */
442 if (i > 1) alt_cyls= alt_heads= alt_secs= 0;
445 void geometry(void)
446 /* Find out the geometry of the device by querying the driver, or by looking
447 * at the partition table. These numbers are crosschecked to make sure that
448 * the geometry is correct. Master bootstraps other than the Minix one use
449 * the CHS numbers in the partition table to load the bootstrap of the active
450 * partition.
453 struct stat dst;
454 int err= 0;
455 struct part_geom geometry;
457 if (submerged) {
458 /* Geometry already known. */
459 sort();
460 return;
462 precise= 0;
463 cylinders= 0;
464 recompute0();
465 if (device < 0) return;
467 /* Try to guess the geometry from the partition table. */
468 guess_geometry();
470 /* Try to get the geometry from the driver. */
471 (void) fstat(device, &dst);
473 if (S_ISBLK(dst.st_mode) || S_ISCHR(dst.st_mode)) {
474 /* Try to get the drive's geometry from the driver. */
476 if (ioctl(device, DIOCGETP, &geometry) < 0)
477 err= errno;
478 else {
479 table[0].lowsec= (unsigned long)(geometry.base /
480 SECTOR_SIZE);
481 table[0].size= (unsigned long)(geometry.size /
482 SECTOR_SIZE);
483 cylinders= geometry.cylinders;
484 heads= geometry.heads;
485 sectors= geometry.sectors;
486 precise= 1;
488 } else {
489 err= ENODEV;
492 if (err != 0) {
493 /* Getting the geometry from the driver failed, so use the
494 * alternate geometry.
496 if (alt_heads == 0) {
497 alt_cyls= table[0].size / (64 * 32);
498 alt_heads= 64;
499 alt_secs= 32;
502 cylinders= alt_cyls;
503 heads= alt_heads;
504 sectors= alt_secs;
506 stat_start(1);
507 printf("Failure to get the geometry of %s: %s", curdev->name,
508 errno == ENOTTY ? "No driver support" : strerror(err));
509 stat_end(5);
510 stat_start(0);
511 printf("The geometry has been guessed as %ux%ux%u",
512 cylinders, heads, sectors);
513 stat_end(5);
514 } else {
515 if (alt_heads == 0) {
516 alt_cyls= cylinders;
517 alt_heads= heads;
518 alt_secs= sectors;
521 if (heads != alt_heads || sectors != alt_secs) {
522 stat_start(1);
523 printf("WARNING:");
524 stat_end(10);
525 stat_start(0);
526 printf(
527 "The %ux%ux%u geometry obtained from the device driver does not match",
528 cylinders, heads, sectors);
529 stat_end(10);
530 stat_start(0);
531 printf(
532 "the %ux%ux%u geometry implied by the partition table. Hit 'X' to switch",
533 alt_cyls, alt_heads, alt_secs);
534 stat_end(10);
535 stat_start(0);
536 printf(
537 "between the two geometries to see what is best. Note that the geometry");
538 stat_end(10);
539 stat_start(0);
540 printf(
541 "must be correct when the table is written or the system may not boot!");
542 stat_end(10);
546 /* Show the base and size of the device instead of the whole drive.
547 * This makes sense for subpartitioning primary partitions.
549 if (precise && ioctl(device, DIOCGETP, &geometry) >= 0) {
550 table[0].lowsec= (unsigned long)(geometry.base / SECTOR_SIZE);
551 table[0].size = (unsigned long)(geometry.size / SECTOR_SIZE);
552 } else {
553 precise= 0;
555 recompute0();
556 sort();
559 typedef struct indicators { /* Partition type to partition name. */
560 unsigned char ind;
561 char name[10];
562 } indicators_t;
564 indicators_t ind_table[]= {
565 { 0x00, "None" },
566 { 0x01, "FAT-12" },
567 { 0x02, "XENIX /" },
568 { 0x03, "XENIX usr" },
569 { 0x04, "FAT-16" },
570 { 0x05, "EXTENDED" },
571 { 0x06, "FAT-16" },
572 { 0x07, "HPFS/NTFS" },
573 { 0x08, "AIX" },
574 { 0x09, "COHERENT" },
575 { 0x0A, "OS/2" },
576 { 0x0B, "FAT-32" },
577 { 0x0C, "FAT?" },
578 { 0x0E, "FAT?" },
579 { 0x0F, "EXTENDED" },
580 { 0x10, "OPUS" },
581 { 0x40, "VENIX286" },
582 { 0x42, "W2000 Dyn" },
583 { 0x52, "MICROPORT" },
584 { 0x63, "386/IX" },
585 { 0x64, "NOVELL286" },
586 { 0x65, "NOVELL386" },
587 { 0x75, "PC/IX" },
588 { 0x80, "MINIX-OLD" },
589 { 0x81, "MINIX" },
590 { 0x82, "LINUXswap" },
591 { 0x83, "LINUX" },
592 { 0x93, "AMOEBA" },
593 { 0x94, "AMOEBAbad" },
594 { 0xA5, "386BSD" },
595 { 0xB7, "BSDI" },
596 { 0xB8, "BSDI swap" },
597 { 0xC7, "SYRINX" },
598 { 0xDB, "CPM" },
599 { 0xFF, "BADBLOCKS" },
602 char *typ2txt(int ind)
603 /* Translate a numeric partition indicator for human eyes. */
605 indicators_t *pind;
607 for (pind= ind_table; pind < arraylimit(ind_table); pind++) {
608 if (pind->ind == ind) return pind->name;
610 return "";
613 int round_sysind(int ind, int delta)
614 /* Find the next known partition type starting with ind in direction delta. */
616 indicators_t *pind;
618 ind= (ind + delta) & 0xFF;
620 if (delta < 0) {
621 for (pind= arraylimit(ind_table)-1; pind->ind > ind; pind--) {}
622 } else {
623 for (pind= ind_table; pind->ind < ind; pind++) {}
625 return pind->ind;
628 /* Objects on the screen, either simple pieces of the text or the cylinder
629 * number of the start of partition three.
631 typedef enum objtype {
632 O_INFO, O_TEXT, O_DEV, O_SUB,
633 O_TYPTXT, O_SORT, O_NUM, O_TYPHEX,
634 O_CYL, O_HEAD, O_SEC,
635 O_SCYL, O_SHEAD, O_SSEC, O_LCYL, O_LHEAD, O_LSEC, O_BASE, O_SIZE, O_KB
636 } objtype_t;
638 #define rjust(type) ((type) >= O_TYPHEX)
639 #define computed(type) ((type) >= O_TYPTXT)
641 typedef struct object {
642 struct object *next;
643 objtype_t type; /* Text field, cylinder number, etc. */
644 char flags; /* Modifiable? */
645 char row;
646 char col;
647 char len;
648 struct part_entry *entry; /* What does the object refer to? */
649 char *text;
650 char value[20]; /* Value when printed. */
651 } object_t;
653 #define OF_MOD 0x01 /* Object value is modifiable. */
654 #define OF_ODD 0x02 /* It has a somewhat odd value. */
655 #define OF_BAD 0x04 /* Its value is no good at all. */
657 /* Events: (Keypress events are the value of the key pressed.) */
658 #define E_ENTER (-1) /* Cursor moves onto object. */
659 #define E_LEAVE (-2) /* Cursor leaves object. */
660 #define E_WRITE (-3) /* Write, but not by typing 'w'. */
662 /* The O_SIZE objects have a dual identity. */
663 enum howend { SIZE, LAST } howend= SIZE;
665 object_t *world= nil;
666 object_t *curobj= nil;
668 object_t *newobject(objtype_t type, int flags, int row, int col, int len)
669 /* Make a new object given a type, flags, position and length on the screen. */
671 object_t *new;
672 object_t **aop= &world;
674 new= alloc(sizeof(*new));
676 new->type= type;
677 new->flags= flags;
678 new->row= row;
679 new->col= col;
680 new->len= len;
681 new->entry= nil;
682 new->text= "";
683 new->value[0]= 0;
685 new->next= *aop;
686 *aop= new;
688 return new;
691 unsigned long entry2base(struct part_entry *pe)
692 /* Return the base sector of the partition if defined. */
694 return pe->sysind == NO_PART ? 0 : pe->lowsec;
697 unsigned long entry2last(struct part_entry *pe)
699 return pe->sysind == NO_PART ? -1 : pe->lowsec + pe->size - 1;
702 unsigned long entry2size(struct part_entry *pe)
704 return pe->sysind == NO_PART ? 0 : pe->size;
707 int overlap(unsigned long sec)
708 /* See if sec is part of another partition. */
710 struct part_entry *pe;
712 for (pe= table + 1; pe <= table + NR_PARTITIONS; pe++) {
713 if (pe->sysind == NO_PART) continue;
715 if (pe->lowsec < sec && sec < pe->lowsec + pe->size)
716 return 1;
718 return 0;
721 int aligned(unsigned long sec, unsigned unit)
722 /* True if sec is aligned to unit or if it is no problem if it is unaligned. */
724 return (offset != 0 && extbase == 0) || (sec % unit == 0);
727 void print(object_t *op)
728 /* Print an object's value if it changed. */
730 struct part_entry *pe= op->entry;
731 int n;
732 unsigned long t;
733 char *name;
734 int oldflags;
735 char oldvalue[20];
737 /* Remember the old flags and value. */
738 oldflags= op->flags;
739 strcpy(oldvalue, op->value);
741 op->flags&= ~(OF_ODD | OF_BAD);
743 switch (op->type) {
744 case O_INFO: {
745 /* Current field. */
746 static struct field { int type; char *name; } fields[]= {
747 { O_DEV, "Select device" },
748 { O_NUM, "Active flag" },
749 { O_TYPHEX, "Hex partition type" },
750 { O_TYPTXT, "Partition type" },
751 { O_SCYL, "Start cylinder" },
752 { O_SHEAD, "Start head" },
753 { O_SSEC, "Start sector" },
754 { O_CYL, "Number of cylinders" },
755 { O_HEAD, "Number of heads" },
756 { O_SEC, "Sectors per track" },
757 { O_LCYL, "Last cylinder" },
758 { O_LHEAD, "Last head" },
759 { O_LSEC, "Last sector" },
760 { O_BASE, "Base sector" },
761 { O_SIZE, "Size in sectors" },
762 { O_KB, "Size in kilobytes" },
763 { -1, "?" },
765 struct field *fp= fields;
767 while (fp->type >= 0 && fp->type != curobj->type) fp++;
768 strcpy(op->value, fp->name);
769 op->flags|= OF_ODD;
770 break; }
771 case O_TEXT:
772 /* Simple text field. */
773 strcpy(op->value, op->text);
774 break;
775 case O_DEV:
776 case O_SUB:
777 /* Name of currently edited device. */
778 name= op->type == O_DEV ? curdev->name :
779 offset == 0 ? "" : curdev->subname;
780 if ((n= strlen(name)) < op->len) n= op->len;
781 strcpy(op->value, name + (n - op->len));
782 if (device < 0 && op->type == O_DEV) op->flags|= OF_BAD;
783 break;
784 case O_NUM:
785 /* Position and active flag. */
786 sprintf(op->value, "%d%c", (int) (pe - table - 1),
787 pe->bootind & ACTIVE_FLAG ? '*' : ' ');
788 break;
789 case O_SORT:
790 /* Position if the driver sorts the table. */
791 sprintf(op->value, "%s%d",
792 curdev->parttype >= PRIMARY ? "p" :
793 curdev->parttype == SUBPART ? "s" : "",
794 (curdev->parttype == SUBPART ||
795 curdev->parttype == FLOPPY ? pe - table
796 : sort_index[pe - table]) - 1);
797 break;
798 case O_TYPHEX:
799 /* Hex partition type indicator. */
800 sprintf(op->value, "%02X", pe->sysind);
801 break;
802 case O_TYPTXT:
803 /* Ascii partition type indicator. */
804 strcpy(op->value, typ2txt(pe->sysind));
805 break;
806 case O_SCYL:
807 /* Partition's start cylinder. */
808 sprintf(op->value, "%lu", entry2base(pe) / secpcyl);
809 break;
810 case O_SHEAD:
811 /* Start head. */
812 t= entry2base(pe);
813 sprintf(op->value, "%lu", t % secpcyl / sectors);
814 if (!aligned(t, secpcyl) && t != table[0].lowsec + sectors)
815 op->flags|= OF_ODD;
816 break;
817 case O_SSEC:
818 /* Start sector. */
819 t= entry2base(pe);
820 sprintf(op->value, "%lu", t % sectors);
821 if (!aligned(t, sectors)) op->flags|= OF_ODD;
822 break;
823 case O_CYL:
824 /* Number of cylinders. */
825 sprintf(op->value, "%u", cylinders);
826 break;
827 case O_HEAD:
828 /* Number of heads. */
829 sprintf(op->value, "%u", heads);
830 break;
831 case O_SEC:
832 /* Number of sectors per track. */
833 sprintf(op->value, "%u", sectors);
834 break;
835 case O_LCYL:
836 /* Partition's last cylinder. */
837 t= entry2last(pe);
838 sprintf(op->value, "%lu", t == -1 ? 0 : t / secpcyl);
839 break;
840 case O_LHEAD:
841 /* Partition's last head. */
842 t= entry2last(pe);
843 sprintf(op->value, "%lu", t == -1 ? 0 : t % secpcyl / sectors);
844 if (!aligned(t + 1, secpcyl)) op->flags|= OF_ODD;
845 break;
846 case O_LSEC:
847 /* Partition's last sector. */
848 t= entry2last(pe);
849 if (t == -1) strcpy(op->value, "-1");
850 else sprintf(op->value, "%lu", t % sectors);
851 if (!aligned(t + 1, sectors)) op->flags|= OF_ODD;
852 break;
853 case O_BASE:
854 /* Partition's base sector. */
855 sprintf(op->value, "%lu", entry2base(pe));
856 if (pe->sysind != NO_PART && pe != &table[0]
857 && (pe->lowsec <= table[0].lowsec || overlap(pe->lowsec)))
858 op->flags|= OF_BAD;
859 break;
860 case O_SIZE:
861 /* Size of partitition in sectors. */
862 t= howend == SIZE ? entry2size(pe) : entry2last(pe);
863 sprintf(op->value, "%lu", pe->sysind == NO_PART ? 0 : t);
864 if (pe->sysind != NO_PART && (pe->size == 0
865 || pe->lowsec + pe->size > table[0].lowsec + table[0].size
866 || overlap(pe->lowsec + pe->size)))
867 op->flags|= OF_BAD;
868 break;
869 case O_KB:
870 /* Size of partitition in kilobytes. */
871 sprintf(op->value, "%lu", entry2size(pe) / 2);
872 break;
873 default:
874 sprintf(op->value, "?? %d ??", op->type);
877 if (device < 0 && computed(op->type)) strcpy(op->value, "?");
879 /* If a value overflows the print field then show a blank
880 * reverse video field.
882 if ((n= strlen(op->value)) > op->len) {
883 n= 0;
884 op->flags|= OF_BAD;
887 /* Right or left justified? */
888 if (rjust(op->type)) {
889 memmove(op->value + (op->len - n), op->value, n);
890 memset(op->value, ' ', op->len - n);
891 } else {
892 memset(op->value + n, ' ', op->len - n);
894 op->value[(int) op->len]= 0;
896 if ((op->flags & (OF_ODD | OF_BAD)) == (oldflags & (OF_ODD | OF_BAD))
897 && strcmp(op->value, oldvalue) == 0) {
898 /* The value did not change. */
899 return;
902 set_cursor(op->row, rjust(op->type) ? op->col - (op->len-1) : op->col);
904 if (op->flags & OF_BAD) tputs(t_so, 1, putchr);
905 else
906 if (op->flags & OF_ODD) tputs(t_md, 1, putchr);
907 putstr(op->value);
908 if (op->flags & OF_BAD) tputs(t_se, 1, putchr);
909 else
910 if (op->flags & OF_ODD) tputs(t_me, 1, putchr);
913 void display(void)
914 /* Repaint all objects that changed. */
916 object_t *op;
918 for (op= world; op != nil; op= op->next) print(op);
921 int typing; /* Set if a digit has been typed to set a value. */
922 int magic; /* Changes when using the magic key. */
924 void event(int ev, object_t *op);
926 void m_redraw(int ev, object_t *op)
927 /* Redraw the screen. */
929 object_t *op2;
931 if (ev != ctrl('L')) return;
933 clear_screen();
934 for (op2= world; op2 != nil; op2= op2->next) op2->value[0]= 0;
937 void m_toggle(int ev, object_t *op)
938 /* Toggle between the driver and alternate geometry. */
940 unsigned t;
942 if (ev != 'X') return;
943 if (alt_cyls == cylinders && alt_heads == heads && alt_secs == sectors)
944 return;
946 t= cylinders; cylinders= alt_cyls; alt_cyls= t;
947 t= heads; heads= alt_heads; alt_heads= t;
948 t= sectors; sectors= alt_secs; alt_secs= t;
949 dirty= 1;
950 recompute0();
953 char size_last[]= "Size";
955 void m_orientation(int ev, object_t *op)
957 if (ev != ' ') return;
959 switch (howend) {
960 case SIZE:
961 howend= LAST;
962 strcpy(size_last, "Last");
963 break;
964 case LAST:
965 howend= SIZE;
966 strcpy(size_last, "Size");
970 void m_move(int ev, object_t *op)
971 /* Move to the nearest modifiably object in the intended direction. Objects
972 * on the same row or column are really near.
975 object_t *near, *op2;
976 unsigned dist, d2, dr, dc;
978 if (ev != 'h' && ev != 'j' && ev != 'k' && ev != 'l' && ev != 'H')
979 return;
981 if (device < 0) {
982 /* No device open? Then try to read first. */
983 event('r', op);
984 if (device < 0) return;
987 near= op;
988 dist= -1;
990 for (op2= world; op2 != nil; op2= op2->next) {
991 if (op2 == op || !(op2->flags & OF_MOD)) continue;
993 dr= abs(op2->row - op->row);
994 dc= abs(op2->col - op->col);
996 d2= 25*dr*dr + dc*dc;
997 if (op2->row != op->row && op2->col != op->col) d2+= 1000;
999 switch (ev) {
1000 case 'h': /* Left */
1001 if (op2->col >= op->col) d2= -1;
1002 break;
1003 case 'j': /* Down */
1004 if (op2->row <= op->row) d2= -1;
1005 break;
1006 case 'k': /* Up */
1007 if (op2->row >= op->row) d2= -1;
1008 break;
1009 case 'l': /* Right */
1010 if (op2->col <= op->col) d2= -1;
1011 break;
1012 case 'H': /* Home */
1013 if (op2->type == O_DEV) d2= 0;
1015 if (d2 < dist) { near= op2; dist= d2; }
1017 if (near != op) event(E_LEAVE, op);
1018 event(E_ENTER, near);
1021 void m_updown(int ev, object_t *op)
1022 /* Move a partition table entry up or down. */
1024 int i, j;
1025 struct part_entry tmp;
1026 int tmpx;
1028 if (ev != ctrl('K') && ev != ctrl('J')) return;
1029 if (op->entry == nil) return;
1031 i= op->entry - table;
1032 if (ev == ctrl('K')) {
1033 if (i <= 1) return;
1034 j= i-1;
1035 } else {
1036 if (i >= NR_PARTITIONS) return;
1037 j= i+1;
1040 tmp= table[i]; table[i]= table[j]; table[j]= tmp;
1041 tmpx= existing[i]; existing[i]= existing[j]; existing[j]= tmpx;
1042 sort();
1043 dirty= 1;
1044 event(ev == ctrl('K') ? 'k' : 'j', op);
1047 void m_enter(int ev, object_t *op)
1048 /* We've moved onto this object. */
1050 if (ev != E_ENTER && ev != ' ' && ev != '<' && ev != '>' && ev != 'X')
1051 return;
1052 curobj= op;
1053 typing= 0;
1054 magic= 0;
1057 void m_leave(int ev, object_t *op)
1058 /* About to leave this object. */
1060 if (ev != E_LEAVE) return;
1063 int within(unsigned *var, unsigned low, unsigned value, unsigned high)
1064 /* Only set *var to value if it looks reasonable. */
1066 if (low <= value && value <= high) {
1067 *var= value;
1068 return 1;
1069 } else
1070 return 0;
1073 int lwithin(unsigned long *var, unsigned long low, unsigned long value,
1074 unsigned long high)
1076 if (low <= value && value <= high) {
1077 *var= value;
1078 return 1;
1079 } else
1080 return 0;
1083 int nextdevice(object_t *op, int delta)
1084 /* Select the next or previous device from the device list. */
1086 dev_t rdev;
1088 if (offset != 0) return 0;
1089 if (dirty) event(E_WRITE, op);
1090 if (dirty) return 0;
1092 if (device >= 0) {
1093 (void) close(device);
1094 device= -1;
1096 recompute0();
1098 rdev= curdev->rdev;
1099 if (delta < 0) {
1101 curdev= curdev->prev;
1102 while (delta < -1 && major(curdev->rdev) == major(rdev)
1103 && curdev->rdev < rdev);
1104 } else {
1106 curdev= curdev->next;
1107 while (delta > 1 && major(curdev->rdev) == major(rdev)
1108 && curdev->rdev > rdev);
1110 return 1;
1113 void check_ind(struct part_entry *pe)
1114 /* If there are no other partitions then make this new one active. */
1116 struct part_entry *pe2;
1118 if (pe->sysind != NO_PART) return;
1120 for (pe2= table + 1; pe2 < table + 1 + NR_PARTITIONS; pe2++)
1121 if (pe2->sysind != NO_PART || pe2->bootind & ACTIVE_FLAG) break;
1123 if (pe2 == table + 1 + NR_PARTITIONS) pe->bootind= ACTIVE_FLAG;
1126 int check_existing(struct part_entry *pe)
1127 /* Check and if not ask if an existing partition may be modified. */
1129 static int expert= 0;
1130 int c;
1132 if (expert || pe == nil || !existing[pe - table]) return 1;
1134 stat_start(1);
1135 putstr("Do you wish to modify existing partitions? (y/n) ");
1136 fflush(stdout);
1137 while ((c= getchar()) != 'y' && c != 'n') {}
1138 putchr(c);
1139 stat_end(3);
1140 return (expert= (c == 'y'));
1143 void m_modify(int ev, object_t *op)
1144 /* Increment, decrement, set, or toggle the value of an object, using
1145 * arithmetic tricks the author doesn't understand either.
1148 object_t *op2;
1149 struct part_entry *pe= op->entry;
1150 int mul, delta;
1151 unsigned level= 1;
1152 unsigned long surplus;
1153 int radix= op->type == O_TYPHEX ? 0x10 : 10;
1154 unsigned long t;
1156 if (device < 0 && op->type != O_DEV) return;
1158 switch (ev) {
1159 case '-':
1160 mul= radix; delta= -1; typing= 0;
1161 break;
1162 case '+':
1163 mul= radix; delta= 1; typing= 0;
1164 break;
1165 case '\b':
1166 if (!typing) return;
1167 mul= 1; delta= 0;
1168 break;
1169 case '\r':
1170 typing= 0;
1171 return;
1172 default:
1173 if ('0' <= ev && ev <= '9')
1174 delta= ev - '0';
1175 else
1176 if (radix == 0x10 && 'a' <= ev && ev <= 'f')
1177 delta= ev - 'a' + 10;
1178 else
1179 if (radix == 0x10 && 'A' <= ev && ev <= 'F')
1180 delta= ev - 'A' + 10;
1181 else
1182 return;
1184 mul= typing ? radix*radix : 0;
1185 typing= 1;
1187 magic= 0;
1189 if (!check_existing(pe)) return;
1191 switch (op->type) {
1192 case O_DEV:
1193 if (ev != '-' && ev != '+') return;
1194 if (!nextdevice(op, delta)) return;
1195 break;
1196 case O_CYL:
1197 if (!within(&cylinders, 1,
1198 cylinders * mul / radix + delta, 1024)) return;
1199 recompute0();
1200 break;
1201 case O_HEAD:
1202 if (!within(&heads, 1, heads * mul / radix + delta, 255))
1203 return;
1204 recompute0();
1205 break;
1206 case O_SEC:
1207 if (!within(&sectors, 1, sectors * mul / radix + delta, 63))
1208 return;
1209 recompute0();
1210 break;
1211 case O_NUM:
1212 if (ev != '-' && ev != '+') return;
1213 for (op2= world; op2 != nil; op2= op2->next) {
1214 if (op2->type == O_NUM && ev == '+')
1215 op2->entry->bootind= 0;
1217 op->entry->bootind= ev == '+' ? ACTIVE_FLAG : 0;
1218 break;
1219 case O_TYPHEX:
1220 check_ind(pe);
1221 pe->sysind= pe->sysind * mul / radix + delta;
1222 break;
1223 case O_TYPTXT:
1224 if (ev != '-' && ev != '+') return;
1225 check_ind(pe);
1226 pe->sysind= round_sysind(pe->sysind, delta);
1227 break;
1228 case O_SCYL:
1229 level= heads;
1230 case O_SHEAD:
1231 level*= sectors;
1232 case O_SSEC:
1233 if (op->type != O_SCYL && ev != '-' && ev != '+') return;
1234 case O_BASE:
1235 if (pe->sysind == NO_PART) memset(pe, 0, sizeof(*pe));
1236 t= pe->lowsec;
1237 surplus= t % level;
1238 if (!lwithin(&t, 0L,
1239 (t / level * mul / radix + delta) * level + surplus,
1240 MAXSIZE)) return;
1241 if (howend == LAST || op->type != O_BASE)
1242 pe->size-= t - pe->lowsec;
1243 pe->lowsec= t;
1244 check_ind(pe);
1245 if (pe->sysind == NO_PART) pe->sysind= MINIX_PART;
1246 break;
1247 case O_LCYL:
1248 level= heads;
1249 case O_LHEAD:
1250 level*= sectors;
1251 case O_LSEC:
1252 if (op->type != O_LCYL && ev != '-' && ev != '+') return;
1254 if (pe->sysind == NO_PART) memset(pe, 0, sizeof(*pe));
1255 t= pe->lowsec + pe->size - 1 + level;
1256 surplus= t % level - mul / radix * level;
1257 if (!lwithin(&t, 0L,
1258 (t / level * mul / radix + delta) * level + surplus,
1259 MAXSIZE)) return;
1260 pe->size= t - pe->lowsec + 1;
1261 check_ind(pe);
1262 if (pe->sysind == NO_PART) pe->sysind= MINIX_PART;
1263 break;
1264 case O_KB:
1265 level= 2;
1266 if (mul == 0) pe->size= 0; /* new value, no surplus */
1267 case O_SIZE:
1268 if (pe->sysind == NO_PART) {
1269 if (op->type == O_KB || howend == SIZE) {
1270 /* First let loose magic to set the base. */
1271 event('m', op);
1272 magic= 0;
1273 pe->size= 0;
1274 event(ev, op);
1275 return;
1277 memset(pe, 0, sizeof(*pe));
1279 t= (op->type == O_KB || howend == SIZE) ? pe->size
1280 : pe->lowsec + pe->size - 1;
1281 surplus= t % level;
1282 if (!lwithin(&t, 0L,
1283 (t / level * mul / radix + delta) * level + surplus,
1284 MAXSIZE)) return;
1285 pe->size= (op->type == O_KB || howend == SIZE) ? t :
1286 t - pe->lowsec + 1;
1287 check_ind(pe);
1288 if (pe->sysind == NO_PART) pe->sysind= MINIX_PART;
1289 break;
1290 default:
1291 return;
1294 /* The order among the entries may have changed. */
1295 sort();
1296 dirty= 1;
1299 unsigned long spell[3 + 4 * (1+NR_PARTITIONS)];
1300 int nspells;
1301 objtype_t touching;
1303 void newspell(unsigned long charm)
1304 /* Add a new spell, descending order for the base, ascending for the size. */
1306 int i, j;
1308 if (charm - table[0].lowsec > table[0].size) return;
1310 for (i= 0; i < nspells; i++) {
1311 if (charm == spell[i]) return; /* duplicate */
1313 if (touching == O_BASE) {
1314 if (charm == table[0].lowsec + table[0].size) return;
1315 if ((spell[0] - charm) < (spell[0] - spell[i])) break;
1316 } else {
1317 if (charm == table[0].lowsec) return;
1318 if ((charm - spell[0]) < (spell[i] - spell[0])) break;
1321 for (j= ++nspells; j > i; j--) spell[j]= spell[j-1];
1322 spell[i]= charm;
1325 void m_magic(int ev, object_t *op)
1326 /* Apply magic onto a base or size number. */
1328 struct part_entry *pe= op->entry, *pe2;
1329 int rough= (offset != 0 && extbase == 0);
1331 if (ev != 'm' || device < 0) return;
1332 typing= 0;
1334 if (!check_existing(pe)) return;
1336 if (magic == 0) {
1337 /* See what magic we can let loose on this value. */
1338 nspells= 1;
1340 /* First spell, the current value. */
1341 switch (op->type) {
1342 case O_SCYL:
1343 case O_SHEAD: /* Start of partition. */
1344 case O_SSEC:
1345 case O_BASE:
1346 touching= O_BASE;
1347 spell[0]= pe->lowsec;
1348 break;
1349 case O_LCYL:
1350 case O_LHEAD:
1351 case O_LSEC: /* End of partition. */
1352 case O_KB:
1353 case O_SIZE:
1354 touching= O_SIZE;
1355 spell[0]= pe->lowsec + pe->size;
1356 break;
1357 default:
1358 return;
1360 if (pe->sysind == NO_PART) {
1361 memset(pe, 0, sizeof(*pe));
1362 check_ind(pe);
1363 pe->sysind= MINIX_PART;
1364 spell[0]= 0;
1365 if (touching == O_SIZE) {
1366 /* First let loose magic on the base. */
1367 object_t *op2;
1369 for (op2= world; op2 != nil; op2= op2->next) {
1370 if (op2->row == op->row &&
1371 op2->type == O_BASE) {
1372 event('m', op2);
1375 magic= 0;
1376 event('m', op);
1377 return;
1380 /* Avoid the first sector on the device. */
1381 if (spell[0] == table[0].lowsec) newspell(spell[0] + 1);
1383 /* Further interesting values are the the bases of other
1384 * partitions or their ends.
1386 for (pe2= table; pe2 < table + 1 + NR_PARTITIONS; pe2++) {
1387 if (pe2 == pe || pe2->sysind == NO_PART) continue;
1388 if (pe2->lowsec == table[0].lowsec)
1389 newspell(table[0].lowsec + 1);
1390 else
1391 newspell(pe2->lowsec);
1392 newspell(pe2->lowsec + pe2->size);
1393 if (touching == O_BASE && howend == SIZE) {
1394 newspell(pe2->lowsec - pe->size);
1395 newspell(pe2->lowsec + pe2->size - pe->size);
1397 if (pe2->lowsec % sectors != 0) rough= 1;
1399 /* Present values rounded up to the next cylinder unless
1400 * the table is already a mess. Use "start + 1 track" instead
1401 * of "start + 1 cylinder". Also add the end of the last
1402 * cylinder.
1404 if (!rough) {
1405 unsigned long n= spell[0];
1406 if (n == table[0].lowsec) n++;
1407 n= (n + sectors - 1) / sectors * sectors;
1408 if (n != table[0].lowsec + sectors)
1409 n= (n + secpcyl - 1) / secpcyl * secpcyl;
1410 newspell(n);
1411 if (touching == O_SIZE)
1412 newspell(table[0].size / secpcyl * secpcyl);
1415 /* Magic has been applied, a spell needs to be chosen. */
1417 if (++magic == nspells) magic= 0;
1419 if (touching == O_BASE) {
1420 if (howend == LAST) pe->size-= spell[magic] - pe->lowsec;
1421 pe->lowsec= spell[magic];
1422 } else
1423 pe->size= spell[magic] - pe->lowsec;
1425 /* The order among the entries may have changed. */
1426 sort();
1427 dirty= 1;
1430 typedef struct diving {
1431 struct diving *up;
1432 struct part_entry old0;
1433 char *oldsubname;
1434 parttype_t oldparttype;
1435 unsigned long oldoffset;
1436 unsigned long oldextbase;
1437 } diving_t;
1439 diving_t *diving= nil;
1441 void m_in(int ev, object_t *op)
1442 /* Go down into a primary or extended partition. */
1444 diving_t *newdiv;
1445 struct part_entry *pe= op->entry, ext;
1446 int n;
1448 if (ev != '>' || device < 0 || pe == nil || pe == &table[0]
1449 || (!(pe->sysind == MINIX_PART && offset == 0)
1450 && !ext_part(pe->sysind))
1451 || pe->size == 0) return;
1453 ext= *pe;
1454 if (extbase != 0) ext.size= extbase + extsize - ext.lowsec;
1456 if (dirty) event(E_WRITE, op);
1457 if (dirty) return;
1458 if (device >= 0) { close(device); device= -1; }
1460 newdiv= alloc(sizeof(*newdiv));
1461 newdiv->old0= table[0];
1462 newdiv->oldsubname= curdev->subname;
1463 newdiv->oldparttype= curdev->parttype;
1464 newdiv->oldoffset= offset;
1465 newdiv->oldextbase= extbase;
1466 newdiv->up= diving;
1467 diving= newdiv;
1469 table[0]= ext;
1471 n= strlen(diving->oldsubname);
1472 curdev->subname= alloc((n + 3) * sizeof(curdev->subname[0]));
1473 strcpy(curdev->subname, diving->oldsubname);
1474 curdev->subname[n++]= ':';
1475 curdev->subname[n++]= '0' + (pe - table - 1);
1476 curdev->subname[n]= 0;
1478 curdev->parttype= curdev->parttype == PRIMARY ? SUBPART : DUNNO;
1479 offset= ext.lowsec;
1480 if (ext_part(ext.sysind) && extbase == 0) {
1481 extbase= ext.lowsec;
1482 extsize= ext.size;
1483 curdev->parttype= DUNNO;
1486 submerged= 1;
1487 event('r', op);
1490 void m_out(int ev, object_t *op)
1491 /* Go up from an extended or subpartition table to its enclosing. */
1493 diving_t *olddiv;
1495 if (ev != '<' || diving == nil) return;
1497 if (dirty) event(E_WRITE, op);
1498 if (dirty) return;
1499 if (device >= 0) { close(device); device= -1; }
1501 olddiv= diving;
1502 diving= olddiv->up;
1504 table[0]= olddiv->old0;
1506 free(curdev->subname);
1507 curdev->subname= olddiv->oldsubname;
1509 curdev->parttype= olddiv->oldparttype;
1510 offset= olddiv->oldoffset;
1511 extbase= olddiv->oldextbase;
1513 free(olddiv);
1515 event('r', op);
1516 if (diving == nil) submerged= 0; /* We surfaced. */
1519 void installboot(unsigned char *bootblock, char *masterboot)
1520 /* Install code from a master bootstrap into a boot block. */
1522 FILE *mfp;
1523 unsigned char buf[SECTOR_SIZE];
1524 int n;
1525 char *err;
1527 if ((mfp= fopen(masterboot, "r")) == nil) {
1528 err= strerror(errno);
1529 goto m_err;
1532 n= fread(buf, sizeof(char), SECTOR_SIZE, mfp);
1533 if (ferror(mfp)) {
1534 err= strerror(errno);
1535 fclose(mfp);
1536 goto m_err;
1538 else if (n < 256) {
1539 err= "Is probably not a boot sector, too small";
1540 fclose(mfp);
1541 goto m_err;
1543 else if (n < SECTOR_SIZE && n > PART_TABLE_OFF) {
1544 /* if only code, it cannot override partition table */
1545 err= "Does not fit in a boot sector";
1546 fclose(mfp);
1547 goto m_err;
1549 else if (n == SECTOR_SIZE) {
1550 if (buf[510] != 0x55 || buf[511] != 0xaa) {
1551 err= "Is not a boot sector (bad magic)";
1552 fclose(mfp);
1553 goto m_err;
1555 n = PART_TABLE_OFF;
1558 if (n > PART_TABLE_OFF) {
1559 err= "Does not fit in a boot sector";
1560 fclose(mfp);
1561 goto m_err;
1564 memcpy(bootblock, buf, n);
1565 fclose(mfp);
1567 /* Bootstrap installed. */
1568 return;
1570 m_err:
1571 stat_start(1);
1572 printf("%s: %s", masterboot, err);
1573 stat_end(5);
1576 ssize_t boot_readwrite(int rw)
1577 /* Read (0) or write (1) the boot sector. */
1579 int r = 0;
1581 if (lseek(device, (off_t)offset * SECTOR_SIZE, SEEK_SET) < 0)
1582 return -1;
1584 switch (rw) {
1585 case 0: r= read(device, bootblock, SECTOR_SIZE); break;
1586 case 1: r= write(device, bootblock, SECTOR_SIZE); break;
1589 return r;
1592 void m_read(int ev, object_t *op)
1593 /* Read the partition table from the current device. */
1595 int i, mode, n;
1596 struct part_entry *pe;
1598 if (ev != 'r' || device >= 0) return;
1600 /* Open() may cause kernel messages: */
1601 stat_start(0);
1602 fflush(stdout);
1604 if (((device= open(curdev->name, mode= O_RDWR, 0666)) < 0
1605 && (errno != EACCES
1606 || (device= open(curdev->name, mode= O_RDONLY)) < 0))
1608 stat_start(1);
1609 printf("%s: %s", curdev->name, strerror(errno));
1610 stat_end(5);
1611 if (device >= 0) { close(device); device= -1; }
1612 return;
1615 /* Assume up to five lines of kernel messages. */
1616 statusrow+= 5-1;
1617 stat_end(5);
1619 if (mode == O_RDONLY) {
1620 stat_start(1);
1621 printf("%s: Readonly", curdev->name);
1622 stat_end(5);
1624 memset(bootblock, 0, sizeof(bootblock));
1626 n= boot_readwrite(0);
1628 if (n <= 0) stat_start(1);
1629 if (n < 0) {
1630 printf("%s: %s", curdev->name, strerror(errno));
1631 close(device);
1632 device= -1;
1633 } else
1634 if (n < SECTOR_SIZE) printf("%s: Unexpected EOF", curdev->subname);
1635 if (n <= 0) stat_end(5);
1637 if (n < SECTOR_SIZE) n= SECTOR_SIZE;
1639 memcpy(table+1, bootblock+PART_TABLE_OFF,
1640 NR_PARTITIONS * sizeof(table[1]));
1641 for (i= 1; i <= NR_PARTITIONS; i++) {
1642 if ((table[i].bootind & ~ACTIVE_FLAG) != 0) break;
1644 if (i <= NR_PARTITIONS || bootblock[510] != 0x55
1645 || bootblock[511] != 0xAA) {
1646 /* Invalid boot block, install bootstrap, wipe partition table.
1648 memset(bootblock, 0, sizeof(bootblock));
1649 installboot(bootblock, MASTERBOOT);
1650 memset(table+1, 0, NR_PARTITIONS * sizeof(table[1]));
1651 stat_start(1);
1652 printf("%s: Invalid partition table (reset)", curdev->subname);
1653 stat_end(5);
1656 /* Fix an extended partition table up to something mere mortals can
1657 * understand. Record already defined partitions.
1659 for (i= 1; i <= NR_PARTITIONS; i++) {
1660 pe= &table[i];
1661 if (extbase != 0 && pe->sysind != NO_PART)
1662 pe->lowsec+= ext_part(pe->sysind) ? extbase : offset;
1663 existing[i]= pe->sysind != NO_PART;
1665 geometry();
1666 dirty= 0;
1668 /* Warn about grave dangers ahead. */
1669 if (extbase != 0) {
1670 stat_start(1);
1671 printf("Warning: You are in an extended partition.");
1672 stat_end(5);
1676 void m_write(int ev, object_t *op)
1677 /* Write the partition table back if modified. */
1679 int c;
1680 struct part_entry new_table[NR_PARTITIONS], *pe;
1682 if (ev != 'w' && ev != E_WRITE) return;
1683 if (device < 0) { dirty= 0; return; }
1684 if (!dirty) {
1685 if (ev == 'w') {
1686 stat_start(1);
1687 printf("%s is not changed, or has already been written",
1688 curdev->subname);
1689 stat_end(2);
1691 return;
1694 if (bootblock[510] != 0x55 || bootblock[511] != 0xAA) {
1695 /* Invalid boot block, warn user. */
1696 stat_start(1);
1697 printf("Warning: About to write a new table on %s",
1698 curdev->subname);
1699 stat_end(5);
1701 if (extbase != 0) {
1702 /* Will this stop the luser? Probably not... */
1703 stat_start(1);
1704 printf("You have changed an extended partition. Bad Idea.");
1705 stat_end(5);
1707 stat_start(1);
1708 putstr("Save partition table? (y/n) ");
1709 fflush(stdout);
1711 while ((c= getchar()) != 'y' && c != 'n' && c != ctrl('?')) {}
1713 if (c == ctrl('?')) putstr("DEL"); else putchr(c);
1714 stat_end(5);
1715 if (c == 'n' && ev == E_WRITE) dirty= 0;
1716 if (c != 'y') return;
1718 memcpy(new_table, table+1, NR_PARTITIONS * sizeof(table[1]));
1719 for (pe= new_table; pe < new_table + NR_PARTITIONS; pe++) {
1720 if (pe->sysind == NO_PART) {
1721 memset(pe, 0, sizeof(*pe));
1722 } else {
1723 abs2dos(&pe->start_head, pe->lowsec);
1724 abs2dos(&pe->last_head, pe->lowsec + pe->size - 1);
1726 /* Fear and loathing time: */
1727 if (extbase != 0)
1728 pe->lowsec-= ext_part(pe->sysind)
1729 ? extbase : offset;
1732 memcpy(bootblock+PART_TABLE_OFF, new_table, sizeof(new_table));
1733 bootblock[510]= 0x55;
1734 bootblock[511]= 0xAA;
1736 if (boot_readwrite(1) < 0) {
1737 stat_start(1);
1738 printf("%s: %s", curdev->name, strerror(errno));
1739 stat_end(5);
1740 return;
1742 dirty= 0;
1745 void m_shell(int ev, object_t *op)
1746 /* Shell escape, to do calculations for instance. */
1748 int r, pid, status;
1749 void (*sigint)(int), (*sigquit)(int), (*sigterm)(int);
1751 if (ev != 's') return;
1753 reset_tty();
1754 fflush(stdout);
1756 switch (pid= fork()) {
1757 case -1:
1758 stat_start(1);
1759 printf("can't fork: %s\n", strerror(errno));
1760 stat_end(3);
1761 break;
1762 case 0:
1763 if (device >= 0) (void) close(device);
1764 execl("/bin/sh", "sh", (char *) nil);
1765 r= errno;
1766 stat_start(1);
1767 printf("/bin/sh: %s\n", strerror(errno));
1768 stat_end(3);
1769 exit(127);
1771 sigint= signal(SIGINT, SIG_IGN);
1772 sigquit= signal(SIGQUIT, SIG_IGN);
1773 sigterm= signal(SIGTERM, SIG_IGN);
1774 while (pid >= 0 && (r= wait(&status)) >= 0 && r != pid) {}
1775 (void) signal(SIGINT, sigint);
1776 (void) signal(SIGQUIT, sigquit);
1777 (void) signal(SIGTERM, sigterm);
1778 tty_raw();
1779 if (pid < 0)
1781 else
1782 if (WIFEXITED(status) && WEXITSTATUS(status) == 127)
1783 stat_start(0); /* Match the stat_start in the child. */
1784 else
1785 event(ctrl('L'), op);
1788 void m_dump(int ev, object_t *op)
1789 /* Raw dump of the partition table. */
1791 struct part_entry table[NR_PARTITIONS], *pe;
1792 int i;
1793 unsigned chs[3];
1795 if (ev != 'p' || device < 0) return;
1797 memcpy(table, bootblock+PART_TABLE_OFF,
1798 NR_PARTITIONS * sizeof(table[0]));
1799 for (i= 0; i < NR_PARTITIONS; i++) {
1800 pe= &table[i];
1801 stat_start(0);
1802 dos2chs(&pe->start_head, chs);
1803 printf("%2d%c %02X%15d%5d%4d",
1804 i+1,
1805 pe->bootind & ACTIVE_FLAG ? '*' : ' ',
1806 pe->sysind,
1807 chs[0], chs[1], chs[2]);
1808 dos2chs(&pe->last_head, chs);
1809 printf("%6d%5d%4d%10u%10u%9u",
1810 chs[0], chs[1], chs[2],
1811 pe->lowsec,
1812 howend == SIZE ? pe->size : pe->size + pe->lowsec - 1,
1813 pe->size / 2);
1814 stat_end(10);
1816 stat_start(0);
1817 printf("(Raw dump of the original %.40s table)",
1818 curdev->subname);
1819 stat_end(10);
1822 int quitting= 0;
1824 void m_quit(int ev, object_t *op)
1825 /* Write the partition table if modified and exit. */
1827 if (ev != 'q' && ev != 'x') return;
1829 quitting= 1;
1831 if (dirty) event(E_WRITE, op);
1832 if (dirty) quitting= 0;
1835 void m_help(int ev, object_t *op)
1836 /* For people without a clue; let's hope they can find the '?' key. */
1838 static struct help {
1839 char *keys;
1840 char *what;
1841 } help[]= {
1842 { "? !", "This help / more advice!" },
1843 { "+ - (= _ PgUp PgDn)","Select/increment/decrement/make active" },
1844 { "0-9 (a-f)", "Enter value" },
1845 { "hjkl (arrow keys)", "Move around" },
1846 { "CTRL-K CTRL-J", "Move entry up/down" },
1847 { "CTRL-L", "Redraw screen" },
1848 { ">", "Start a subpartition table" },
1849 { "<", "Back to the primary partition table" },
1850 { "m", "Cycle through magic values" },
1851 { "spacebar", "Show \"Size\" or \"Last\"" },
1852 { "r w", "Read/write partition table" },
1853 { "p s q x", "Raw dump / Shell escape / Quit / Exit" },
1854 { "y n DEL", "Answer \"yes\", \"no\", \"cancel\"" },
1856 static char *advice[] = {
1857 "* Choose a disk with '+' and '-', then hit 'r'.",
1858 "* To change any value: Move to it and use '+', '-' or type the desired value.",
1859 "* To make a new partition: Move over to the Size or Kb field of an unused",
1860 " partition and type the size. Hit the 'm' key to pad the partition out to",
1861 " a cylinder boundary. Hit 'm' again to pad it out to the end of the disk.",
1862 " You can hit 'm' more than once on a base or size field to see several",
1863 " interesting values go by. Note: Other Operating Systems can be picky about",
1864 " partitions that are not padded to cylinder boundaries. Look for highlighted",
1865 " head or sector numbers.",
1866 "* To reuse a partition: Change the type to MINIX.",
1867 "* To delete a partition: Type a zero in the hex Type field.",
1868 "* To make a partition active: Type '+' in the Num field.",
1869 "* To study the list of keys: Type '?'.",
1872 if (ev == '?') {
1873 struct help *hp;
1875 for (hp= help; hp < arraylimit(help); hp++) {
1876 stat_start(0);
1877 printf("%-25s - %s", hp->keys, hp->what);
1878 stat_end(0);
1880 stat_start(0);
1881 putstr("Things like ");
1882 putstr(t_so); putstr("this"); putstr(t_se);
1883 putstr(" must be checked, but ");
1884 putstr(t_md); putstr("this"); putstr(t_me);
1885 putstr(" is not really a problem");
1886 stat_end(0);
1887 } else
1888 if (ev == '!') {
1889 char **ap;
1891 for (ap= advice; ap < arraylimit(advice); ap++) {
1892 stat_start(0);
1893 putstr(*ap);
1894 stat_end(0);
1899 void event(int ev, object_t *op)
1900 /* Simply call all modifiers for an event, each one knows when to act. */
1902 m_help(ev, op);
1903 m_redraw(ev, op);
1904 m_toggle(ev, op);
1905 m_orientation(ev, op);
1906 m_move(ev, op);
1907 m_updown(ev, op);
1908 m_enter(ev, op);
1909 m_leave(ev, op);
1910 m_modify(ev, op);
1911 m_magic(ev, op);
1912 m_in(ev, op);
1913 m_out(ev, op);
1914 m_read(ev, op);
1915 m_write(ev, op);
1916 m_shell(ev, op);
1917 m_dump(ev, op);
1918 m_quit(ev, op);
1921 int keypress(void)
1922 /* Get a single keypress. Translate compound keypresses (arrow keys) to
1923 * their simpler equivalents.
1926 char ch;
1927 int c;
1928 int esc= 0;
1930 set_cursor(curobj->row, curobj->col);
1931 fflush(stdout);
1933 do {
1934 if (read(0, &ch, sizeof(ch)) < 0) fatal("stdin");
1935 c= (unsigned char) ch;
1936 switch (esc) {
1937 case 0:
1938 switch (c) {
1939 case ctrl('['): esc= 1; break;
1940 case '_': c= '-'; break;
1941 case '=': c= '+'; break;
1943 break;
1944 case 1:
1945 esc= c == '[' ? 2 : 0;
1946 break;
1947 case 2:
1948 switch (c) {
1949 case 'D': c= 'h'; break;
1950 case 'B': c= 'j'; break;
1951 case 'A': c= 'k'; break;
1952 case 'C': c= 'l'; break;
1953 case 'H': c= 'H'; break;
1954 case 'U':
1955 case 'S': c= '-'; break;
1956 case 'V':
1957 case 'T': c= '+'; break;
1959 /*FALL THROUGH*/
1960 default:
1961 esc= 0;
1963 } while (esc > 0);
1965 switch (c) {
1966 case ctrl('B'): c= 'h'; break;
1967 case ctrl('N'): c= 'j'; break;
1968 case ctrl('P'): c= 'k'; break;
1969 case ctrl('F'): c= 'l'; break;
1972 return c;
1975 void mainloop(void)
1976 /* Get keypress, handle event, display results, reset screen, ad infinitum. */
1978 int key;
1980 while (!quitting) {
1981 stat_reset();
1983 key= keypress();
1985 event(key, curobj);
1987 display();
1991 int main(int argc, char **argv)
1993 object_t *op;
1994 int i, r, key;
1995 struct part_entry *pe;
1997 /* Define a few objects to show on the screen. First text: */
1998 op= newobject(O_INFO, 0, 0, 2, 19);
1999 op= newobject(O_TEXT, 0, 0, 22, 13); op->text= "----first----";
2000 op= newobject(O_TEXT, 0, 0, 37, 13); op->text= "--geom/last--";
2001 op= newobject(O_TEXT, 0, 0, 52, 18); op->text= "------sectors-----";
2002 op= newobject(O_TEXT, 0, 1, 4, 6); op->text= "Device";
2003 op= newobject(O_TEXT, 0, 1, 23, 12); op->text= "Cyl Head Sec";
2004 op= newobject(O_TEXT, 0, 1, 38, 12); op->text= "Cyl Head Sec";
2005 op= newobject(O_TEXT, 0, 1, 56, 4); op->text= "Base";
2006 op= newobject(O_TEXT, 0, 1, 66, 4); op->text= size_last;
2007 op= newobject(O_TEXT, 0, 1, 78, 2); op->text= "Kb";
2008 op= newobject(O_TEXT, 0, 4, 0, 15); op->text= "Num Sort Type";
2010 /* The device is the current object: */
2011 curobj= newobject(O_DEV, OF_MOD, 2, 4, 15);
2012 op= newobject(O_SUB, 0, 3, 4, 15);
2014 /* Geometry: */
2015 op= newobject(O_CYL, OF_MOD, 2, 40, 5); op->entry= &table[0];
2016 op= newobject(O_HEAD, OF_MOD, 2, 45, 3); op->entry= &table[0];
2017 op= newobject(O_SEC, OF_MOD, 2, 49, 2); op->entry= &table[0];
2019 /* Objects for the device: */
2020 op= newobject(O_SCYL, 0, 3, 25, 5); op->entry= &table[0];
2021 op= newobject(O_SHEAD, 0, 3, 30, 3); op->entry= &table[0];
2022 op= newobject(O_SSEC, 0, 3, 34, 2); op->entry= &table[0];
2023 op= newobject(O_LCYL, 0, 3, 40, 5); op->entry= &table[0];
2024 op= newobject(O_LHEAD, 0, 3, 45, 3); op->entry= &table[0];
2025 op= newobject(O_LSEC, 0, 3, 49, 2); op->entry= &table[0];
2026 op= newobject(O_BASE, 0, 3, 59, 9); op->entry= &table[0];
2027 op= newobject(O_SIZE, 0, 3, 69, 9); op->entry= &table[0];
2028 op= newobject(O_KB, 0, 3, 79, 9); op->entry= &table[0];
2030 /* Objects for each partition: */
2031 for (r= 5, pe= table+1; pe <= table+NR_PARTITIONS; r++, pe++) {
2032 op= newobject(O_NUM, OF_MOD, r, 1, 2); op->entry= pe;
2033 op= newobject(O_SORT, 0, r, 5, 2); op->entry= pe;
2034 op= newobject(O_TYPHEX, OF_MOD, r, 10, 2); op->entry= pe;
2035 op= newobject(O_TYPTXT, OF_MOD, r, 12, 9); op->entry= pe;
2036 op= newobject(O_SCYL, OF_MOD, r, 25, 5); op->entry= pe;
2037 op= newobject(O_SHEAD, OF_MOD, r, 30, 3); op->entry= pe;
2038 op= newobject(O_SSEC, OF_MOD, r, 34, 2); op->entry= pe;
2039 op= newobject(O_LCYL, OF_MOD, r, 40, 5); op->entry= pe;
2040 op= newobject(O_LHEAD, OF_MOD, r, 45, 3); op->entry= pe;
2041 op= newobject(O_LSEC, OF_MOD, r, 49, 2); op->entry= pe;
2042 op= newobject(O_BASE, OF_MOD, r, 59, 9); op->entry= pe;
2043 op= newobject(O_SIZE, OF_MOD, r, 69, 9); op->entry= pe;
2044 op= newobject(O_KB, OF_MOD, r, 79, 9); op->entry= pe;
2047 for (i= 1; i < argc; i++) newdevice(argv[i], 0);
2049 if (firstdev == nil) {
2050 getdevices();
2051 key= ctrl('L');
2052 } else {
2053 key= 'r';
2056 if (firstdev != nil) {
2057 init_tty();
2058 clear_screen();
2059 event(key, curobj);
2060 display();
2061 mainloop();
2062 reset_tty();
2064 exit(0);