libc, libutil: remove compat hacks
[minix.git] / commands / part / part.c
blob18b3ee53a08ac7ac90634fbec61bc68e9166fe3f
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 int putchr(int c)
126 return 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 if (t == -1) strcpy(op->value, "-1");
856 else sprintf(op->value, "%lu", t % sectors);
857 if (!aligned(t + 1, sectors)) op->flags|= OF_ODD;
858 break;
859 case O_BASE:
860 /* Partition's base sector. */
861 sprintf(op->value, "%lu", entry2base(pe));
862 if (pe->sysind != NO_PART && pe != &table[0]
863 && (pe->lowsec <= table[0].lowsec || overlap(pe->lowsec)))
864 op->flags|= OF_BAD;
865 break;
866 case O_SIZE:
867 /* Size of partitition in sectors. */
868 t= howend == SIZE ? entry2size(pe) : entry2last(pe);
869 sprintf(op->value, "%lu", pe->sysind == NO_PART ? 0 : t);
870 if (pe->sysind != NO_PART && (pe->size == 0
871 || pe->lowsec + pe->size > table[0].lowsec + table[0].size
872 || overlap(pe->lowsec + pe->size)))
873 op->flags|= OF_BAD;
874 break;
875 case O_KB:
876 /* Size of partitition in kilobytes. */
877 sprintf(op->value, "%lu", entry2size(pe) / 2);
878 break;
879 default:
880 sprintf(op->value, "?? %d ??", op->type);
883 if (device < 0 && computed(op->type)) strcpy(op->value, "?");
885 /* If a value overflows the print field then show a blank
886 * reverse video field.
888 if ((n= strlen(op->value)) > op->len) {
889 n= 0;
890 op->flags|= OF_BAD;
893 /* Right or left justified? */
894 if (rjust(op->type)) {
895 memmove(op->value + (op->len - n), op->value, n);
896 memset(op->value, ' ', op->len - n);
897 } else {
898 memset(op->value + n, ' ', op->len - n);
900 op->value[(int) op->len]= 0;
902 if ((op->flags & (OF_ODD | OF_BAD)) == (oldflags & (OF_ODD | OF_BAD))
903 && strcmp(op->value, oldvalue) == 0) {
904 /* The value did not change. */
905 return;
908 set_cursor(op->row, rjust(op->type) ? op->col - (op->len-1) : op->col);
910 if (op->flags & OF_BAD) tputs(t_so, 1, putchr);
911 else
912 if (op->flags & OF_ODD) tputs(t_md, 1, putchr);
913 putstr(op->value);
914 if (op->flags & OF_BAD) tputs(t_se, 1, putchr);
915 else
916 if (op->flags & OF_ODD) tputs(t_me, 1, putchr);
919 void display(void)
920 /* Repaint all objects that changed. */
922 object_t *op;
924 for (op= world; op != nil; op= op->next) print(op);
927 int typing; /* Set if a digit has been typed to set a value. */
928 int magic; /* Changes when using the magic key. */
930 void event(int ev, object_t *op);
932 void m_redraw(int ev, object_t *op)
933 /* Redraw the screen. */
935 object_t *op2;
937 if (ev != ctrl('L')) return;
939 clear_screen();
940 for (op2= world; op2 != nil; op2= op2->next) op2->value[0]= 0;
943 void m_toggle(int ev, object_t *op)
944 /* Toggle between the driver and alternate geometry. */
946 unsigned t;
948 if (ev != 'X') return;
949 if (alt_cyls == cylinders && alt_heads == heads && alt_secs == sectors)
950 return;
952 t= cylinders; cylinders= alt_cyls; alt_cyls= t;
953 t= heads; heads= alt_heads; alt_heads= t;
954 t= sectors; sectors= alt_secs; alt_secs= t;
955 dirty= 1;
956 recompute0();
959 char size_last[]= "Size";
961 void m_orientation(int ev, object_t *op)
963 if (ev != ' ') return;
965 switch (howend) {
966 case SIZE:
967 howend= LAST;
968 strcpy(size_last, "Last");
969 break;
970 case LAST:
971 howend= SIZE;
972 strcpy(size_last, "Size");
976 void m_move(int ev, object_t *op)
977 /* Move to the nearest modifiably object in the intended direction. Objects
978 * on the same row or column are really near.
981 object_t *near, *op2;
982 unsigned dist, d2, dr, dc;
984 if (ev != 'h' && ev != 'j' && ev != 'k' && ev != 'l' && ev != 'H')
985 return;
987 if (device < 0) {
988 /* No device open? Then try to read first. */
989 event('r', op);
990 if (device < 0) return;
993 near= op;
994 dist= -1;
996 for (op2= world; op2 != nil; op2= op2->next) {
997 if (op2 == op || !(op2->flags & OF_MOD)) continue;
999 dr= abs(op2->row - op->row);
1000 dc= abs(op2->col - op->col);
1002 d2= 25*dr*dr + dc*dc;
1003 if (op2->row != op->row && op2->col != op->col) d2+= 1000;
1005 switch (ev) {
1006 case 'h': /* Left */
1007 if (op2->col >= op->col) d2= -1;
1008 break;
1009 case 'j': /* Down */
1010 if (op2->row <= op->row) d2= -1;
1011 break;
1012 case 'k': /* Up */
1013 if (op2->row >= op->row) d2= -1;
1014 break;
1015 case 'l': /* Right */
1016 if (op2->col <= op->col) d2= -1;
1017 break;
1018 case 'H': /* Home */
1019 if (op2->type == O_DEV) d2= 0;
1021 if (d2 < dist) { near= op2; dist= d2; }
1023 if (near != op) event(E_LEAVE, op);
1024 event(E_ENTER, near);
1027 void m_updown(int ev, object_t *op)
1028 /* Move a partition table entry up or down. */
1030 int i, j;
1031 struct part_entry tmp;
1032 int tmpx;
1034 if (ev != ctrl('K') && ev != ctrl('J')) return;
1035 if (op->entry == nil) return;
1037 i= op->entry - table;
1038 if (ev == ctrl('K')) {
1039 if (i <= 1) return;
1040 j= i-1;
1041 } else {
1042 if (i >= NR_PARTITIONS) return;
1043 j= i+1;
1046 tmp= table[i]; table[i]= table[j]; table[j]= tmp;
1047 tmpx= existing[i]; existing[i]= existing[j]; existing[j]= tmpx;
1048 sort();
1049 dirty= 1;
1050 event(ev == ctrl('K') ? 'k' : 'j', op);
1053 void m_enter(int ev, object_t *op)
1054 /* We've moved onto this object. */
1056 if (ev != E_ENTER && ev != ' ' && ev != '<' && ev != '>' && ev != 'X')
1057 return;
1058 curobj= op;
1059 typing= 0;
1060 magic= 0;
1063 void m_leave(int ev, object_t *op)
1064 /* About to leave this object. */
1066 if (ev != E_LEAVE) return;
1069 int within(unsigned *var, unsigned low, unsigned value, unsigned high)
1070 /* Only set *var to value if it looks reasonable. */
1072 if (low <= value && value <= high) {
1073 *var= value;
1074 return 1;
1075 } else
1076 return 0;
1079 int lwithin(unsigned long *var, unsigned long low, unsigned long value,
1080 unsigned long high)
1082 if (low <= value && value <= high) {
1083 *var= value;
1084 return 1;
1085 } else
1086 return 0;
1089 int nextdevice(object_t *op, int delta)
1090 /* Select the next or previous device from the device list. */
1092 dev_t rdev;
1094 if (offset != 0) return 0;
1095 if (dirty) event(E_WRITE, op);
1096 if (dirty) return 0;
1098 if (device >= 0) {
1099 (void) close(device);
1100 device= -1;
1102 recompute0();
1104 rdev= curdev->rdev;
1105 if (delta < 0) {
1107 curdev= curdev->prev;
1108 while (delta < -1 && major(curdev->rdev) == major(rdev)
1109 && curdev->rdev < rdev);
1110 } else {
1112 curdev= curdev->next;
1113 while (delta > 1 && major(curdev->rdev) == major(rdev)
1114 && curdev->rdev > rdev);
1116 return 1;
1119 void check_ind(struct part_entry *pe)
1120 /* If there are no other partitions then make this new one active. */
1122 struct part_entry *pe2;
1124 if (pe->sysind != NO_PART) return;
1126 for (pe2= table + 1; pe2 < table + 1 + NR_PARTITIONS; pe2++)
1127 if (pe2->sysind != NO_PART || pe2->bootind & ACTIVE_FLAG) break;
1129 if (pe2 == table + 1 + NR_PARTITIONS) pe->bootind= ACTIVE_FLAG;
1132 int check_existing(struct part_entry *pe)
1133 /* Check and if not ask if an existing partition may be modified. */
1135 static int expert= 0;
1136 int c;
1138 if (expert || pe == nil || !existing[pe - table]) return 1;
1140 stat_start(1);
1141 putstr("Do you wish to modify existing partitions? (y/n) ");
1142 fflush(stdout);
1143 while ((c= getchar()) != 'y' && c != 'n') {}
1144 putchr(c);
1145 stat_end(3);
1146 return (expert= (c == 'y'));
1149 void m_modify(int ev, object_t *op)
1150 /* Increment, decrement, set, or toggle the value of an object, using
1151 * arithmetic tricks the author doesn't understand either.
1154 object_t *op2;
1155 struct part_entry *pe= op->entry;
1156 int mul, delta;
1157 unsigned level= 1;
1158 unsigned long surplus;
1159 int radix= op->type == O_TYPHEX ? 0x10 : 10;
1160 unsigned long t;
1162 if (device < 0 && op->type != O_DEV) return;
1164 switch (ev) {
1165 case '-':
1166 mul= radix; delta= -1; typing= 0;
1167 break;
1168 case '+':
1169 mul= radix; delta= 1; typing= 0;
1170 break;
1171 case '\b':
1172 if (!typing) return;
1173 mul= 1; delta= 0;
1174 break;
1175 case '\r':
1176 typing= 0;
1177 return;
1178 default:
1179 if ('0' <= ev && ev <= '9')
1180 delta= ev - '0';
1181 else
1182 if (radix == 0x10 && 'a' <= ev && ev <= 'f')
1183 delta= ev - 'a' + 10;
1184 else
1185 if (radix == 0x10 && 'A' <= ev && ev <= 'F')
1186 delta= ev - 'A' + 10;
1187 else
1188 return;
1190 mul= typing ? radix*radix : 0;
1191 typing= 1;
1193 magic= 0;
1195 if (!check_existing(pe)) return;
1197 switch (op->type) {
1198 case O_DEV:
1199 if (ev != '-' && ev != '+') return;
1200 if (!nextdevice(op, delta)) return;
1201 break;
1202 case O_CYL:
1203 if (!within(&cylinders, 1,
1204 cylinders * mul / radix + delta, 1024)) return;
1205 recompute0();
1206 break;
1207 case O_HEAD:
1208 if (!within(&heads, 1, heads * mul / radix + delta, 255))
1209 return;
1210 recompute0();
1211 break;
1212 case O_SEC:
1213 if (!within(&sectors, 1, sectors * mul / radix + delta, 63))
1214 return;
1215 recompute0();
1216 break;
1217 case O_NUM:
1218 if (ev != '-' && ev != '+') return;
1219 for (op2= world; op2 != nil; op2= op2->next) {
1220 if (op2->type == O_NUM && ev == '+')
1221 op2->entry->bootind= 0;
1223 op->entry->bootind= ev == '+' ? ACTIVE_FLAG : 0;
1224 break;
1225 case O_TYPHEX:
1226 check_ind(pe);
1227 pe->sysind= pe->sysind * mul / radix + delta;
1228 break;
1229 case O_TYPTXT:
1230 if (ev != '-' && ev != '+') return;
1231 check_ind(pe);
1232 pe->sysind= round_sysind(pe->sysind, delta);
1233 break;
1234 case O_SCYL:
1235 level= heads;
1236 case O_SHEAD:
1237 level*= sectors;
1238 case O_SSEC:
1239 if (op->type != O_SCYL && ev != '-' && ev != '+') return;
1240 case O_BASE:
1241 if (pe->sysind == NO_PART) memset(pe, 0, sizeof(*pe));
1242 t= pe->lowsec;
1243 surplus= t % level;
1244 if (!lwithin(&t, 0L,
1245 (t / level * mul / radix + delta) * level + surplus,
1246 MAXSIZE)) return;
1247 if (howend == LAST || op->type != O_BASE)
1248 pe->size-= t - pe->lowsec;
1249 pe->lowsec= t;
1250 check_ind(pe);
1251 if (pe->sysind == NO_PART) pe->sysind= MINIX_PART;
1252 break;
1253 case O_LCYL:
1254 level= heads;
1255 case O_LHEAD:
1256 level*= sectors;
1257 case O_LSEC:
1258 if (op->type != O_LCYL && ev != '-' && ev != '+') return;
1260 if (pe->sysind == NO_PART) memset(pe, 0, sizeof(*pe));
1261 t= pe->lowsec + pe->size - 1 + level;
1262 surplus= t % level - mul / radix * level;
1263 if (!lwithin(&t, 0L,
1264 (t / level * mul / radix + delta) * level + surplus,
1265 MAXSIZE)) return;
1266 pe->size= t - pe->lowsec + 1;
1267 check_ind(pe);
1268 if (pe->sysind == NO_PART) pe->sysind= MINIX_PART;
1269 break;
1270 case O_KB:
1271 level= 2;
1272 if (mul == 0) pe->size= 0; /* new value, no surplus */
1273 case O_SIZE:
1274 if (pe->sysind == NO_PART) {
1275 if (op->type == O_KB || howend == SIZE) {
1276 /* First let loose magic to set the base. */
1277 event('m', op);
1278 magic= 0;
1279 pe->size= 0;
1280 event(ev, op);
1281 return;
1283 memset(pe, 0, sizeof(*pe));
1285 t= (op->type == O_KB || howend == SIZE) ? pe->size
1286 : pe->lowsec + pe->size - 1;
1287 surplus= t % level;
1288 if (!lwithin(&t, 0L,
1289 (t / level * mul / radix + delta) * level + surplus,
1290 MAXSIZE)) return;
1291 pe->size= (op->type == O_KB || howend == SIZE) ? t :
1292 t - pe->lowsec + 1;
1293 check_ind(pe);
1294 if (pe->sysind == NO_PART) pe->sysind= MINIX_PART;
1295 break;
1296 default:
1297 return;
1300 /* The order among the entries may have changed. */
1301 sort();
1302 dirty= 1;
1305 unsigned long spell[3 + 4 * (1+NR_PARTITIONS)];
1306 int nspells;
1307 objtype_t touching;
1309 void newspell(unsigned long charm)
1310 /* Add a new spell, descending order for the base, ascending for the size. */
1312 int i, j;
1314 if (charm - table[0].lowsec > table[0].size) return;
1316 for (i= 0; i < nspells; i++) {
1317 if (charm == spell[i]) return; /* duplicate */
1319 if (touching == O_BASE) {
1320 if (charm == table[0].lowsec + table[0].size) return;
1321 if ((spell[0] - charm) < (spell[0] - spell[i])) break;
1322 } else {
1323 if (charm == table[0].lowsec) return;
1324 if ((charm - spell[0]) < (spell[i] - spell[0])) break;
1327 for (j= ++nspells; j > i; j--) spell[j]= spell[j-1];
1328 spell[i]= charm;
1331 void m_magic(int ev, object_t *op)
1332 /* Apply magic onto a base or size number. */
1334 struct part_entry *pe= op->entry, *pe2;
1335 int rough= (offset != 0 && extbase == 0);
1337 if (ev != 'm' || device < 0) return;
1338 typing= 0;
1340 if (!check_existing(pe)) return;
1342 if (magic == 0) {
1343 /* See what magic we can let loose on this value. */
1344 nspells= 1;
1346 /* First spell, the current value. */
1347 switch (op->type) {
1348 case O_SCYL:
1349 case O_SHEAD: /* Start of partition. */
1350 case O_SSEC:
1351 case O_BASE:
1352 touching= O_BASE;
1353 spell[0]= pe->lowsec;
1354 break;
1355 case O_LCYL:
1356 case O_LHEAD:
1357 case O_LSEC: /* End of partition. */
1358 case O_KB:
1359 case O_SIZE:
1360 touching= O_SIZE;
1361 spell[0]= pe->lowsec + pe->size;
1362 break;
1363 default:
1364 return;
1366 if (pe->sysind == NO_PART) {
1367 memset(pe, 0, sizeof(*pe));
1368 check_ind(pe);
1369 pe->sysind= MINIX_PART;
1370 spell[0]= 0;
1371 if (touching == O_SIZE) {
1372 /* First let loose magic on the base. */
1373 object_t *op2;
1375 for (op2= world; op2 != nil; op2= op2->next) {
1376 if (op2->row == op->row &&
1377 op2->type == O_BASE) {
1378 event('m', op2);
1381 magic= 0;
1382 event('m', op);
1383 return;
1386 /* Avoid the first sector on the device. */
1387 if (spell[0] == table[0].lowsec) newspell(spell[0] + 1);
1389 /* Further interesting values are the the bases of other
1390 * partitions or their ends.
1392 for (pe2= table; pe2 < table + 1 + NR_PARTITIONS; pe2++) {
1393 if (pe2 == pe || pe2->sysind == NO_PART) continue;
1394 if (pe2->lowsec == table[0].lowsec)
1395 newspell(table[0].lowsec + 1);
1396 else
1397 newspell(pe2->lowsec);
1398 newspell(pe2->lowsec + pe2->size);
1399 if (touching == O_BASE && howend == SIZE) {
1400 newspell(pe2->lowsec - pe->size);
1401 newspell(pe2->lowsec + pe2->size - pe->size);
1403 if (pe2->lowsec % sectors != 0) rough= 1;
1405 /* Present values rounded up to the next cylinder unless
1406 * the table is already a mess. Use "start + 1 track" instead
1407 * of "start + 1 cylinder". Also add the end of the last
1408 * cylinder.
1410 if (!rough) {
1411 unsigned long n= spell[0];
1412 if (n == table[0].lowsec) n++;
1413 n= (n + sectors - 1) / sectors * sectors;
1414 if (n != table[0].lowsec + sectors)
1415 n= (n + secpcyl - 1) / secpcyl * secpcyl;
1416 newspell(n);
1417 if (touching == O_SIZE)
1418 newspell(table[0].size / secpcyl * secpcyl);
1421 /* Magic has been applied, a spell needs to be chosen. */
1423 if (++magic == nspells) magic= 0;
1425 if (touching == O_BASE) {
1426 if (howend == LAST) pe->size-= spell[magic] - pe->lowsec;
1427 pe->lowsec= spell[magic];
1428 } else
1429 pe->size= spell[magic] - pe->lowsec;
1431 /* The order among the entries may have changed. */
1432 sort();
1433 dirty= 1;
1436 typedef struct diving {
1437 struct diving *up;
1438 struct part_entry old0;
1439 char *oldsubname;
1440 parttype_t oldparttype;
1441 unsigned long oldoffset;
1442 unsigned long oldextbase;
1443 } diving_t;
1445 diving_t *diving= nil;
1447 void m_in(int ev, object_t *op)
1448 /* Go down into a primary or extended partition. */
1450 diving_t *newdiv;
1451 struct part_entry *pe= op->entry, ext;
1452 int n;
1454 if (ev != '>' || device < 0 || pe == nil || pe == &table[0]
1455 || (!(pe->sysind == MINIX_PART && offset == 0)
1456 && !ext_part(pe->sysind))
1457 || pe->size == 0) return;
1459 ext= *pe;
1460 if (extbase != 0) ext.size= extbase + extsize - ext.lowsec;
1462 if (dirty) event(E_WRITE, op);
1463 if (dirty) return;
1464 if (device >= 0) { close(device); device= -1; }
1466 newdiv= alloc(sizeof(*newdiv));
1467 newdiv->old0= table[0];
1468 newdiv->oldsubname= curdev->subname;
1469 newdiv->oldparttype= curdev->parttype;
1470 newdiv->oldoffset= offset;
1471 newdiv->oldextbase= extbase;
1472 newdiv->up= diving;
1473 diving= newdiv;
1475 table[0]= ext;
1477 n= strlen(diving->oldsubname);
1478 curdev->subname= alloc((n + 3) * sizeof(curdev->subname[0]));
1479 strcpy(curdev->subname, diving->oldsubname);
1480 curdev->subname[n++]= ':';
1481 curdev->subname[n++]= '0' + (pe - table - 1);
1482 curdev->subname[n]= 0;
1484 curdev->parttype= curdev->parttype == PRIMARY ? SUBPART : DUNNO;
1485 offset= ext.lowsec;
1486 if (ext_part(ext.sysind) && extbase == 0) {
1487 extbase= ext.lowsec;
1488 extsize= ext.size;
1489 curdev->parttype= DUNNO;
1492 submerged= 1;
1493 event('r', op);
1496 void m_out(int ev, object_t *op)
1497 /* Go up from an extended or subpartition table to its enclosing. */
1499 diving_t *olddiv;
1501 if (ev != '<' || diving == nil) return;
1503 if (dirty) event(E_WRITE, op);
1504 if (dirty) return;
1505 if (device >= 0) { close(device); device= -1; }
1507 olddiv= diving;
1508 diving= olddiv->up;
1510 table[0]= olddiv->old0;
1512 free(curdev->subname);
1513 curdev->subname= olddiv->oldsubname;
1515 curdev->parttype= olddiv->oldparttype;
1516 offset= olddiv->oldoffset;
1517 extbase= olddiv->oldextbase;
1519 free(olddiv);
1521 event('r', op);
1522 if (diving == nil) submerged= 0; /* We surfaced. */
1525 void installboot(unsigned char *bootblock, char *masterboot)
1526 /* Install code from a master bootstrap into a boot block. */
1528 FILE *mfp;
1529 unsigned char buf[SECTOR_SIZE];
1530 int n;
1531 char *err;
1533 if ((mfp= fopen(masterboot, "r")) == nil) {
1534 err= strerror(errno);
1535 goto m_err;
1538 n= fread(buf, sizeof(char), SECTOR_SIZE, mfp);
1539 if (ferror(mfp)) {
1540 err= strerror(errno);
1541 fclose(mfp);
1542 goto m_err;
1544 else if (n < 256) {
1545 err= "Is probably not a boot sector, too small";
1546 fclose(mfp);
1547 goto m_err;
1549 else if (n < SECTOR_SIZE && n > PART_TABLE_OFF) {
1550 /* if only code, it cannot override partition table */
1551 err= "Does not fit in a boot sector";
1552 fclose(mfp);
1553 goto m_err;
1555 else if (n == SECTOR_SIZE) {
1556 if (buf[510] != 0x55 || buf[511] != 0xaa) {
1557 err= "Is not a boot sector (bad magic)";
1558 fclose(mfp);
1559 goto m_err;
1561 n = PART_TABLE_OFF;
1564 if (n > PART_TABLE_OFF) {
1565 err= "Does not fit in a boot sector";
1566 fclose(mfp);
1567 goto m_err;
1570 memcpy(bootblock, buf, n);
1571 fclose(mfp);
1573 /* Bootstrap installed. */
1574 return;
1576 m_err:
1577 stat_start(1);
1578 printf("%s: %s", masterboot, err);
1579 stat_end(5);
1582 ssize_t boot_readwrite(int rw)
1583 /* Read (0) or write (1) the boot sector. */
1585 int r = 0;
1587 if (lseek64(device, (u64_t) offset * SECTOR_SIZE, SEEK_SET, NULL) < 0)
1588 return -1;
1590 switch (rw) {
1591 case 0: r= read(device, bootblock, SECTOR_SIZE); break;
1592 case 1: r= write(device, bootblock, SECTOR_SIZE); break;
1595 return r;
1598 void m_read(int ev, object_t *op)
1599 /* Read the partition table from the current device. */
1601 int i, mode, n;
1602 struct part_entry *pe;
1604 if (ev != 'r' || device >= 0) return;
1606 /* Open() may cause kernel messages: */
1607 stat_start(0);
1608 fflush(stdout);
1610 if (((device= open(curdev->name, mode= O_RDWR, 0666)) < 0
1611 && (errno != EACCES
1612 || (device= open(curdev->name, mode= O_RDONLY)) < 0))
1614 stat_start(1);
1615 printf("%s: %s", curdev->name, strerror(errno));
1616 stat_end(5);
1617 if (device >= 0) { close(device); device= -1; }
1618 return;
1621 /* Assume up to five lines of kernel messages. */
1622 statusrow+= 5-1;
1623 stat_end(5);
1625 if (mode == O_RDONLY) {
1626 stat_start(1);
1627 printf("%s: Readonly", curdev->name);
1628 stat_end(5);
1630 memset(bootblock, 0, sizeof(bootblock));
1632 n= boot_readwrite(0);
1634 if (n <= 0) stat_start(1);
1635 if (n < 0) {
1636 printf("%s: %s", curdev->name, strerror(errno));
1637 close(device);
1638 device= -1;
1639 } else
1640 if (n < SECTOR_SIZE) printf("%s: Unexpected EOF", curdev->subname);
1641 if (n <= 0) stat_end(5);
1643 if (n < SECTOR_SIZE) n= SECTOR_SIZE;
1645 memcpy(table+1, bootblock+PART_TABLE_OFF,
1646 NR_PARTITIONS * sizeof(table[1]));
1647 for (i= 1; i <= NR_PARTITIONS; i++) {
1648 if ((table[i].bootind & ~ACTIVE_FLAG) != 0) break;
1650 if (i <= NR_PARTITIONS || bootblock[510] != 0x55
1651 || bootblock[511] != 0xAA) {
1652 /* Invalid boot block, install bootstrap, wipe partition table.
1654 memset(bootblock, 0, sizeof(bootblock));
1655 installboot(bootblock, MASTERBOOT);
1656 memset(table+1, 0, NR_PARTITIONS * sizeof(table[1]));
1657 stat_start(1);
1658 printf("%s: Invalid partition table (reset)", curdev->subname);
1659 stat_end(5);
1662 /* Fix an extended partition table up to something mere mortals can
1663 * understand. Record already defined partitions.
1665 for (i= 1; i <= NR_PARTITIONS; i++) {
1666 pe= &table[i];
1667 if (extbase != 0 && pe->sysind != NO_PART)
1668 pe->lowsec+= ext_part(pe->sysind) ? extbase : offset;
1669 existing[i]= pe->sysind != NO_PART;
1671 geometry();
1672 dirty= 0;
1674 /* Warn about grave dangers ahead. */
1675 if (extbase != 0) {
1676 stat_start(1);
1677 printf("Warning: You are in an extended partition.");
1678 stat_end(5);
1682 void m_write(int ev, object_t *op)
1683 /* Write the partition table back if modified. */
1685 int c;
1686 struct part_entry new_table[NR_PARTITIONS], *pe;
1688 if (ev != 'w' && ev != E_WRITE) return;
1689 if (device < 0) { dirty= 0; return; }
1690 if (!dirty) {
1691 if (ev == 'w') {
1692 stat_start(1);
1693 printf("%s is not changed, or has already been written",
1694 curdev->subname);
1695 stat_end(2);
1697 return;
1700 if (bootblock[510] != 0x55 || bootblock[511] != 0xAA) {
1701 /* Invalid boot block, warn user. */
1702 stat_start(1);
1703 printf("Warning: About to write a new table on %s",
1704 curdev->subname);
1705 stat_end(5);
1707 if (extbase != 0) {
1708 /* Will this stop the luser? Probably not... */
1709 stat_start(1);
1710 printf("You have changed an extended partition. Bad Idea.");
1711 stat_end(5);
1713 stat_start(1);
1714 putstr("Save partition table? (y/n) ");
1715 fflush(stdout);
1717 while ((c= getchar()) != 'y' && c != 'n' && c != ctrl('?')) {}
1719 if (c == ctrl('?')) putstr("DEL"); else putchr(c);
1720 stat_end(5);
1721 if (c == 'n' && ev == E_WRITE) dirty= 0;
1722 if (c != 'y') return;
1724 memcpy(new_table, table+1, NR_PARTITIONS * sizeof(table[1]));
1725 for (pe= new_table; pe < new_table + NR_PARTITIONS; pe++) {
1726 if (pe->sysind == NO_PART) {
1727 memset(pe, 0, sizeof(*pe));
1728 } else {
1729 abs2dos(&pe->start_head, pe->lowsec);
1730 abs2dos(&pe->last_head, pe->lowsec + pe->size - 1);
1732 /* Fear and loathing time: */
1733 if (extbase != 0)
1734 pe->lowsec-= ext_part(pe->sysind)
1735 ? extbase : offset;
1738 memcpy(bootblock+PART_TABLE_OFF, new_table, sizeof(new_table));
1739 bootblock[510]= 0x55;
1740 bootblock[511]= 0xAA;
1742 if (boot_readwrite(1) < 0) {
1743 stat_start(1);
1744 printf("%s: %s", curdev->name, strerror(errno));
1745 stat_end(5);
1746 return;
1748 dirty= 0;
1751 void m_shell(int ev, object_t *op)
1752 /* Shell escape, to do calculations for instance. */
1754 int r, pid, status;
1755 void (*sigint)(int), (*sigquit)(int), (*sigterm)(int);
1757 if (ev != 's') return;
1759 reset_tty();
1760 fflush(stdout);
1762 switch (pid= fork()) {
1763 case -1:
1764 stat_start(1);
1765 printf("can't fork: %s\n", strerror(errno));
1766 stat_end(3);
1767 break;
1768 case 0:
1769 if (device >= 0) (void) close(device);
1770 execl("/bin/sh", "sh", (char *) nil);
1771 r= errno;
1772 stat_start(1);
1773 printf("/bin/sh: %s\n", strerror(errno));
1774 stat_end(3);
1775 exit(127);
1777 sigint= signal(SIGINT, SIG_IGN);
1778 sigquit= signal(SIGQUIT, SIG_IGN);
1779 sigterm= signal(SIGTERM, SIG_IGN);
1780 while (pid >= 0 && (r= wait(&status)) >= 0 && r != pid) {}
1781 (void) signal(SIGINT, sigint);
1782 (void) signal(SIGQUIT, sigquit);
1783 (void) signal(SIGTERM, sigterm);
1784 tty_raw();
1785 if (pid < 0)
1787 else
1788 if (WIFEXITED(status) && WEXITSTATUS(status) == 127)
1789 stat_start(0); /* Match the stat_start in the child. */
1790 else
1791 event(ctrl('L'), op);
1794 void m_dump(int ev, object_t *op)
1795 /* Raw dump of the partition table. */
1797 struct part_entry table[NR_PARTITIONS], *pe;
1798 int i;
1799 unsigned chs[3];
1801 if (ev != 'p' || device < 0) return;
1803 memcpy(table, bootblock+PART_TABLE_OFF,
1804 NR_PARTITIONS * sizeof(table[0]));
1805 for (i= 0; i < NR_PARTITIONS; i++) {
1806 pe= &table[i];
1807 stat_start(0);
1808 dos2chs(&pe->start_head, chs);
1809 printf("%2d%c %02X%15d%5d%4d",
1810 i+1,
1811 pe->bootind & ACTIVE_FLAG ? '*' : ' ',
1812 pe->sysind,
1813 chs[0], chs[1], chs[2]);
1814 dos2chs(&pe->last_head, chs);
1815 printf("%6d%5d%4d%10lu%10ld%9lu",
1816 chs[0], chs[1], chs[2],
1817 pe->lowsec,
1818 howend == SIZE ? pe->size : pe->size + pe->lowsec - 1,
1819 pe->size / 2);
1820 stat_end(10);
1822 stat_start(0);
1823 printf("(Raw dump of the original %.40s table)",
1824 curdev->subname);
1825 stat_end(10);
1828 int quitting= 0;
1830 void m_quit(int ev, object_t *op)
1831 /* Write the partition table if modified and exit. */
1833 if (ev != 'q' && ev != 'x') return;
1835 quitting= 1;
1837 if (dirty) event(E_WRITE, op);
1838 if (dirty) quitting= 0;
1841 void m_help(int ev, object_t *op)
1842 /* For people without a clue; let's hope they can find the '?' key. */
1844 static struct help {
1845 char *keys;
1846 char *what;
1847 } help[]= {
1848 { "? !", "This help / more advice!" },
1849 { "+ - (= _ PgUp PgDn)","Select/increment/decrement/make active" },
1850 { "0-9 (a-f)", "Enter value" },
1851 { "hjkl (arrow keys)", "Move around" },
1852 { "CTRL-K CTRL-J", "Move entry up/down" },
1853 { "CTRL-L", "Redraw screen" },
1854 { ">", "Start a subpartition table" },
1855 { "<", "Back to the primary partition table" },
1856 { "m", "Cycle through magic values" },
1857 { "spacebar", "Show \"Size\" or \"Last\"" },
1858 { "r w", "Read/write partition table" },
1859 { "p s q x", "Raw dump / Shell escape / Quit / Exit" },
1860 { "y n DEL", "Answer \"yes\", \"no\", \"cancel\"" },
1862 static char *advice[] = {
1863 "* Choose a disk with '+' and '-', then hit 'r'.",
1864 "* To change any value: Move to it and use '+', '-' or type the desired value.",
1865 "* To make a new partition: Move over to the Size or Kb field of an unused",
1866 " partition and type the size. Hit the 'm' key to pad the partition out to",
1867 " a cylinder boundary. Hit 'm' again to pad it out to the end of the disk.",
1868 " You can hit 'm' more than once on a base or size field to see several",
1869 " interesting values go by. Note: Other Operating Systems can be picky about",
1870 " partitions that are not padded to cylinder boundaries. Look for highlighted",
1871 " head or sector numbers.",
1872 "* To reuse a partition: Change the type to MINIX.",
1873 "* To delete a partition: Type a zero in the hex Type field.",
1874 "* To make a partition active: Type '+' in the Num field.",
1875 "* To study the list of keys: Type '?'.",
1878 if (ev == '?') {
1879 struct help *hp;
1881 for (hp= help; hp < arraylimit(help); hp++) {
1882 stat_start(0);
1883 printf("%-25s - %s", hp->keys, hp->what);
1884 stat_end(0);
1886 stat_start(0);
1887 putstr("Things like ");
1888 putstr(t_so); putstr("this"); putstr(t_se);
1889 putstr(" must be checked, but ");
1890 putstr(t_md); putstr("this"); putstr(t_me);
1891 putstr(" is not really a problem");
1892 stat_end(0);
1893 } else
1894 if (ev == '!') {
1895 char **ap;
1897 for (ap= advice; ap < arraylimit(advice); ap++) {
1898 stat_start(0);
1899 putstr(*ap);
1900 stat_end(0);
1905 void event(int ev, object_t *op)
1906 /* Simply call all modifiers for an event, each one knows when to act. */
1908 m_help(ev, op);
1909 m_redraw(ev, op);
1910 m_toggle(ev, op);
1911 m_orientation(ev, op);
1912 m_move(ev, op);
1913 m_updown(ev, op);
1914 m_enter(ev, op);
1915 m_leave(ev, op);
1916 m_modify(ev, op);
1917 m_magic(ev, op);
1918 m_in(ev, op);
1919 m_out(ev, op);
1920 m_read(ev, op);
1921 m_write(ev, op);
1922 m_shell(ev, op);
1923 m_dump(ev, op);
1924 m_quit(ev, op);
1927 int keypress(void)
1928 /* Get a single keypress. Translate compound keypresses (arrow keys) to
1929 * their simpler equivalents.
1932 char ch;
1933 int c;
1934 int esc= 0;
1936 set_cursor(curobj->row, curobj->col);
1937 fflush(stdout);
1939 do {
1940 if (read(0, &ch, sizeof(ch)) < 0) fatal("stdin");
1941 c= (unsigned char) ch;
1942 switch (esc) {
1943 case 0:
1944 switch (c) {
1945 case ctrl('['): esc= 1; break;
1946 case '_': c= '-'; break;
1947 case '=': c= '+'; break;
1949 break;
1950 case 1:
1951 esc= c == '[' ? 2 : 0;
1952 break;
1953 case 2:
1954 switch (c) {
1955 case 'D': c= 'h'; break;
1956 case 'B': c= 'j'; break;
1957 case 'A': c= 'k'; break;
1958 case 'C': c= 'l'; break;
1959 case 'H': c= 'H'; break;
1960 case 'U':
1961 case 'S': c= '-'; break;
1962 case 'V':
1963 case 'T': c= '+'; break;
1965 /*FALL THROUGH*/
1966 default:
1967 esc= 0;
1969 } while (esc > 0);
1971 switch (c) {
1972 case ctrl('B'): c= 'h'; break;
1973 case ctrl('N'): c= 'j'; break;
1974 case ctrl('P'): c= 'k'; break;
1975 case ctrl('F'): c= 'l'; break;
1978 return c;
1981 void mainloop(void)
1982 /* Get keypress, handle event, display results, reset screen, ad infinitum. */
1984 int key;
1986 while (!quitting) {
1987 stat_reset();
1989 key= keypress();
1991 event(key, curobj);
1993 display();
1997 int main(int argc, char **argv)
1999 object_t *op;
2000 int i, r, key;
2001 struct part_entry *pe;
2003 /* Define a few objects to show on the screen. First text: */
2004 op= newobject(O_INFO, 0, 0, 2, 19);
2005 op= newobject(O_TEXT, 0, 0, 22, 13); op->text= "----first----";
2006 op= newobject(O_TEXT, 0, 0, 37, 13); op->text= "--geom/last--";
2007 op= newobject(O_TEXT, 0, 0, 52, 18); op->text= "------sectors-----";
2008 op= newobject(O_TEXT, 0, 1, 4, 6); op->text= "Device";
2009 op= newobject(O_TEXT, 0, 1, 23, 12); op->text= "Cyl Head Sec";
2010 op= newobject(O_TEXT, 0, 1, 38, 12); op->text= "Cyl Head Sec";
2011 op= newobject(O_TEXT, 0, 1, 56, 4); op->text= "Base";
2012 op= newobject(O_TEXT, 0, 1, 66, 4); op->text= size_last;
2013 op= newobject(O_TEXT, 0, 1, 78, 2); op->text= "Kb";
2014 op= newobject(O_TEXT, 0, 4, 0, 15); op->text= "Num Sort Type";
2016 /* The device is the current object: */
2017 curobj= newobject(O_DEV, OF_MOD, 2, 4, 15);
2018 op= newobject(O_SUB, 0, 3, 4, 15);
2020 /* Geometry: */
2021 op= newobject(O_CYL, OF_MOD, 2, 40, 5); op->entry= &table[0];
2022 op= newobject(O_HEAD, OF_MOD, 2, 45, 3); op->entry= &table[0];
2023 op= newobject(O_SEC, OF_MOD, 2, 49, 2); op->entry= &table[0];
2025 /* Objects for the device: */
2026 op= newobject(O_SCYL, 0, 3, 25, 5); op->entry= &table[0];
2027 op= newobject(O_SHEAD, 0, 3, 30, 3); op->entry= &table[0];
2028 op= newobject(O_SSEC, 0, 3, 34, 2); op->entry= &table[0];
2029 op= newobject(O_LCYL, 0, 3, 40, 5); op->entry= &table[0];
2030 op= newobject(O_LHEAD, 0, 3, 45, 3); op->entry= &table[0];
2031 op= newobject(O_LSEC, 0, 3, 49, 2); op->entry= &table[0];
2032 op= newobject(O_BASE, 0, 3, 59, 9); op->entry= &table[0];
2033 op= newobject(O_SIZE, 0, 3, 69, 9); op->entry= &table[0];
2034 op= newobject(O_KB, 0, 3, 79, 9); op->entry= &table[0];
2036 /* Objects for each partition: */
2037 for (r= 5, pe= table+1; pe <= table+NR_PARTITIONS; r++, pe++) {
2038 op= newobject(O_NUM, OF_MOD, r, 1, 2); op->entry= pe;
2039 op= newobject(O_SORT, 0, r, 5, 2); op->entry= pe;
2040 op= newobject(O_TYPHEX, OF_MOD, r, 10, 2); op->entry= pe;
2041 op= newobject(O_TYPTXT, OF_MOD, r, 12, 9); op->entry= pe;
2042 op= newobject(O_SCYL, OF_MOD, r, 25, 5); op->entry= pe;
2043 op= newobject(O_SHEAD, OF_MOD, r, 30, 3); op->entry= pe;
2044 op= newobject(O_SSEC, OF_MOD, r, 34, 2); op->entry= pe;
2045 op= newobject(O_LCYL, OF_MOD, r, 40, 5); op->entry= pe;
2046 op= newobject(O_LHEAD, OF_MOD, r, 45, 3); op->entry= pe;
2047 op= newobject(O_LSEC, OF_MOD, r, 49, 2); op->entry= pe;
2048 op= newobject(O_BASE, OF_MOD, r, 59, 9); op->entry= pe;
2049 op= newobject(O_SIZE, OF_MOD, r, 69, 9); op->entry= pe;
2050 op= newobject(O_KB, OF_MOD, r, 79, 9); op->entry= pe;
2053 for (i= 1; i < argc; i++) newdevice(argv[i], 0);
2055 if (firstdev == nil) {
2056 getdevices();
2057 key= ctrl('L');
2058 } else {
2059 key= 'r';
2062 if (firstdev != nil) {
2063 init_tty();
2064 clear_screen();
2065 event(key, curobj);
2066 display();
2067 mainloop();
2068 reset_tty();
2070 exit(0);