xref: /minix/minix/commands/part/part.c (revision 9f988b79)
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>
27 
28 /* True if a partition is an extended partition. */
29 #define ext_part(s)	((s) == 0x05 || (s) == 0x0F)
30 
31 /* Minix master bootstrap code. */
32 static char MASTERBOOT[] = "/usr/mdec/mbr";
33 
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
44 
45  */
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 */
50 
51 #define arraysize(a)	(sizeof(a) / sizeof((a)[0]))
52 #define arraylimit(a)	((a) + arraysize(a))
53 
54 void report(const char *label)
55 {
56 	fprintf(stderr, "part: %s: %s\n", label, strerror(errno));
57 }
58 
59 void fatal(const char *label)
60 {
61 	report(label);
62 	exit(1);
63 }
64 
65 struct termios termios;
66 
67 void save_ttyflags(void)
68 /* Save tty attributes for later restoration. */
69 {
70 	if (tcgetattr(0, &termios) < 0) fatal("");
71 }
72 
73 void restore_ttyflags(void)
74 /* Reset the tty flags to how we got 'em. */
75 {
76 	if (tcsetattr(0, TCSANOW, &termios) < 0) fatal("");
77 }
78 
79 void tty_raw(void)
80 /* Set the terminal to raw mode, no signals, no echoing. */
81 {
82 	struct termios rawterm;
83 
84 	rawterm= termios;
85 	rawterm.c_lflag &= ~(ICANON|ISIG|ECHO);
86 	rawterm.c_iflag &= ~(ICRNL);
87 	if (tcsetattr(0, TCSANOW, &rawterm) < 0) fatal("");
88 }
89 
90 #define ctrl(c)		((c) == '?' ? '\177' : ((c) & '\37'))
91 
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
95 
96 void init_tty(void)
97 /* Get terminal capabilities and set the tty to "editor" mode. */
98 {
99 	char *term;
100 	static char termbuf[1024];
101 	char *tp;
102 
103 	if ((term= getenv("TERM")) == nil || tgetent(termbuf, term) != 1) {
104 		fprintf(stderr, "part: Can't get terminal capabilities\n");
105 		exit(1);
106 	}
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);
111 	}
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));
118 
119 	save_ttyflags();
120 	tty_raw();
121 }
122 
123 int putchr(int c)
124 {
125 	return putchar(c);
126 }
127 
128 void putstr(char *s)
129 {
130 	int c;
131 
132 	while ((c= *s++) != 0) putchr(c);
133 }
134 
135 void set_cursor(int row, int col)
136 {
137 	tputs(tgoto(t_cm, col, row), 1, putchr);
138 }
139 
140 int statusrow= STATUSROW;
141 int stat_ktl= 1;
142 int need_help= 1;
143 
144 void stat_start(int serious)
145 /* Prepare for printing on a fresh status line, possibly highlighted. */
146 {
147 	set_cursor(statusrow++, 0);
148 	tputs(t_cd, 1, putchr);
149 	if (serious) tputs(t_so, 1, putchr);
150 }
151 
152 void stat_end(int ktl)
153 /* Closing bracket for stat_start.  Sets "keystrokes to live" of message. */
154 {
155 	tputs(t_se, 1, putchr);
156 	stat_ktl= ktl;
157 	need_help= 1;
158 }
159 
160 void stat_reset(void)
161 /* Reset the statusline pointer and clear old messages if expired. */
162 {
163 	if (stat_ktl > 0 && --stat_ktl == 0) {
164 		statusrow= STATUSROW;
165 		need_help= 1;
166 	}
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");
172 	}
173 	statusrow= STATUSROW;
174 	need_help= 0;
175 }
176 
177 void clear_screen(void)
178 {
179 	set_cursor(0, 0);
180 	tputs(t_cd, 1, putchr);
181 	stat_ktl= 1;
182 	stat_reset();
183 }
184 
185 void reset_tty(void)
186 /* Reset the tty to cooked mode. */
187 {
188 	restore_ttyflags();
189 	set_cursor(statusrow, 0);
190 	tputs(t_cd, 1, putchr);
191 }
192 
193 void *alloc(size_t n)
194 {
195 	void *m;
196 
197 	if ((m= malloc(n)) == nil) { reset_tty(); fatal(""); }
198 
199 	return m;
200 }
201 
202 typedef enum parttype { DUNNO, SUBPART, PRIMARY, FLOPPY } parttype_t;
203 
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;
211 
212 device_t *firstdev= nil, *curdev;
213 
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.
217  */
218 {
219 	device_t *new, *nextdev, *prevdev;
220 	struct stat st;
221 
222 	st.st_rdev= 0;
223 	if (scanning) {
224 		if (stat(name, &st) < 0 || !S_ISBLK(st.st_mode)) return;
225 
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;
242 		}
243 		/* Interesting device found. */
244 	} else {
245 		(void) stat(name, &st);
246 	}
247 
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;
260 	}
261 
262 	if (firstdev == nil) {
263 		firstdev= new;
264 		new->next= new->prev= new;
265 		curdev= firstdev;
266 		return;
267 	}
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;
276 
277 	if (new->rdev < firstdev->rdev) firstdev= new;
278 	if (new->rdev == DEV_C0D0) curdev= new;
279 	if (curdev->rdev != DEV_C0D0) curdev= firstdev;
280 }
281 
282 void getdevices(void)
283 /* Get all block devices from /dev that look interesting. */
284 {
285 	DIR *d;
286 	struct dirent *e;
287 	char name[5 + NAME_MAX + 1];
288 
289 	if ((d= opendir("/dev")) == nil) fatal("/dev");
290 
291 	while ((e= readdir(d)) != nil) {
292 		strcpy(name, "/dev/");
293 		strcpy(name + 5, e->d_name);
294 		newdevice(name, 1);
295 	}
296 	(void) closedir(d);
297 }
298 
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;
310 
311 unsigned long sortbase(struct part_entry *pe)
312 {
313 	return pe->sysind == NO_PART ? -1 : pe->lowsec;
314 }
315 
316 void sort(void)
317 /* Let the sort_index array show the order partitions are sorted in. */
318 {
319 	int i, j;
320 	int idx[1 + NR_PARTITIONS];
321 
322 	for (i= 1; i <= NR_PARTITIONS; i++) idx[i]= i;
323 
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];
327 
328 			if (sortbase(&table[sj]) > sortbase(&table[sj1])) {
329 				idx[j]= sj1;
330 				idx[j+1]= sj;
331 			}
332 		}
333 	}
334 	for (i= 1; i <= NR_PARTITIONS; i++) sort_index[idx[i]]= i;
335 }
336 
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.
341  */
342 {
343 	chs[0]= ((dos[1] & 0xC0) << 2) | dos[2];
344 	chs[1]= dos[0];
345 	chs[2]= (dos[1] & 0x3F) - 1;
346 }
347 
348 void abs2dos(unsigned char *dos, unsigned long pos)
349 /* Translate a sector offset to three DOS bytes. */
350 {
351 	unsigned h, c, s;
352 
353 	c= pos / secpcyl;
354 	h= (pos % secpcyl) / sectors;
355 	s= pos % sectors + 1;
356 
357 	dos[0]= h;
358 	dos[1]= s | ((c >> 2) & 0xC0);
359 	dos[2]= c & 0xFF;
360 }
361 
362 void recompute0(void)
363 /* Recompute the partition size for the device after a geometry change. */
364 {
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;
372 	}
373 	table[0].sysind= device < 0 ? NO_PART : MINIX_PART;
374 	secpcyl= heads * sectors;
375 }
376 
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...)
381  */
382 {
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 */
389 
390 	alt_cyls= alt_heads= alt_secs= 0;
391 
392 	/* Initially all possible H/S combinations are possible.  HS[h][0]
393 	 * bit 0 is used to rule out a head value.
394 	 */
395 	for (h= 1; h <= 255; h++) {
396 		for (s= 0; s < 8; s++) HS[h][s]= 0xFF;
397 	}
398 
399 	for (i= 0; i < 2*NR_PARTITIONS; i++) {
400 		pe= &(table+1)[i >> 1];
401 		if (pe->sysind == NO_PART) continue;
402 
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;
410 		}
411 
412 		if (chs[0] >= alt_cyls) alt_cyls= chs[0]+1;
413 
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));
421 				}
422 				if (HS[h][s/8] & (1 << (s%8))) n++;
423 			}
424 			if (n == 0) HS[h][0]= 0;
425 		}
426 	}
427 
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;
437 			}
438 		}
439 	}
440 
441 	/* Forget it if more than one choice... */
442 	if (i > 1) alt_cyls= alt_heads= alt_secs= 0;
443 }
444 
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.
451  */
452 {
453 	struct stat dst;
454 	int err= 0;
455 	struct part_geom geometry;
456 
457 	if (submerged) {
458 		/* Geometry already known. */
459 		sort();
460 		return;
461 	}
462 	precise= 0;
463 	cylinders= 0;
464 	recompute0();
465 	if (device < 0) return;
466 
467 	/* Try to guess the geometry from the partition table. */
468 	guess_geometry();
469 
470 	/* Try to get the geometry from the driver. */
471 	(void) fstat(device, &dst);
472 
473 	if (S_ISBLK(dst.st_mode) || S_ISCHR(dst.st_mode)) {
474 		/* Try to get the drive's geometry from the driver. */
475 
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;
487 		}
488 	} else {
489 		err= ENODEV;
490 	}
491 
492 	if (err != 0) {
493 		/* Getting the geometry from the driver failed, so use the
494 		 * alternate geometry.
495 		 */
496 		if (alt_heads == 0) {
497 			alt_cyls= table[0].size / (64 * 32);
498 			alt_heads= 64;
499 			alt_secs= 32;
500 		}
501 
502 		cylinders= alt_cyls;
503 		heads= alt_heads;
504 		sectors= alt_secs;
505 
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;
519 		}
520 
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);
543 		}
544 	}
545 
546 	/* Show the base and size of the device instead of the whole drive.
547 	 * This makes sense for subpartitioning primary partitions.
548 	 */
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;
554 	}
555 	recompute0();
556 	sort();
557 }
558 
559 typedef struct indicators {	/* Partition type to partition name. */
560 	unsigned char	ind;
561 	char		name[10];
562 } indicators_t;
563 
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"	},
600 };
601 
602 char *typ2txt(int ind)
603 /* Translate a numeric partition indicator for human eyes. */
604 {
605 	indicators_t *pind;
606 
607 	for (pind= ind_table; pind < arraylimit(ind_table); pind++) {
608 		if (pind->ind == ind) return pind->name;
609 	}
610 	return "";
611 }
612 
613 int round_sysind(int ind, int delta)
614 /* Find the next known partition type starting with ind in direction delta. */
615 {
616 	indicators_t *pind;
617 
618 	ind= (ind + delta) & 0xFF;
619 
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++) {}
624 	}
625 	return pind->ind;
626 }
627 
628 /* Objects on the screen, either simple pieces of the text or the cylinder
629  * number of the start of partition three.
630  */
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;
637 
638 #define rjust(type)	((type) >= O_TYPHEX)
639 #define computed(type)	((type) >= O_TYPTXT)
640 
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;
652 
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. */
656 
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'. */
661 
662 /* The O_SIZE objects have a dual identity. */
663 enum howend { SIZE, LAST } howend= SIZE;
664 
665 object_t *world= nil;
666 object_t *curobj= nil;
667 
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. */
670 {
671 	object_t *new;
672 	object_t **aop= &world;
673 
674 	new= alloc(sizeof(*new));
675 
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;
684 
685 	new->next= *aop;
686 	*aop= new;
687 
688 	return new;
689 }
690 
691 unsigned long entry2base(struct part_entry *pe)
692 /* Return the base sector of the partition if defined. */
693 {
694 	return pe->sysind == NO_PART ? 0 : pe->lowsec;
695 }
696 
697 unsigned long entry2last(struct part_entry *pe)
698 {
699 	return pe->sysind == NO_PART ? -1 : pe->lowsec + pe->size - 1;
700 }
701 
702 unsigned long entry2size(struct part_entry *pe)
703 {
704 	return pe->sysind == NO_PART ? 0 : pe->size;
705 }
706 
707 int overlap(unsigned long sec)
708 /* See if sec is part of another partition. */
709 {
710 	struct part_entry *pe;
711 
712 	for (pe= table + 1; pe <= table + NR_PARTITIONS; pe++) {
713 		if (pe->sysind == NO_PART) continue;
714 
715 		if (pe->lowsec < sec && sec < pe->lowsec + pe->size)
716 			return 1;
717 	}
718 	return 0;
719 }
720 
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. */
723 {
724 	return (offset != 0 && extbase == 0) || (sec % unit == 0);
725 }
726 
727 void print(object_t *op)
728 /* Print an object's value if it changed. */
729 {
730 	struct part_entry *pe= op->entry;
731 	int n;
732 	unsigned long t;
733 	char *name;
734 	int oldflags;
735 	char oldvalue[20];
736 
737 	/* Remember the old flags and value. */
738 	oldflags= op->flags;
739 	strcpy(oldvalue, op->value);
740 
741 	op->flags&= ~(OF_ODD | OF_BAD);
742 
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,		"?"			},
764 		};
765 		struct field *fp= fields;
766 
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);
875 	}
876 
877 	if (device < 0 && computed(op->type)) strcpy(op->value, "?");
878 
879 	/* If a value overflows the print field then show a blank
880 	 * reverse video field.
881 	 */
882 	if ((n= strlen(op->value)) > op->len) {
883 		n= 0;
884 		op->flags|= OF_BAD;
885 	}
886 
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);
893 	}
894 	op->value[(int) op->len]= 0;
895 
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;
900 	}
901 
902 	set_cursor(op->row, rjust(op->type) ? op->col - (op->len-1) : op->col);
903 
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);
911 }
912 
913 void display(void)
914 /* Repaint all objects that changed. */
915 {
916 	object_t *op;
917 
918 	for (op= world; op != nil; op= op->next) print(op);
919 }
920 
921 int typing;	/* Set if a digit has been typed to set a value. */
922 int magic;	/* Changes when using the magic key. */
923 
924 void event(int ev, object_t *op);
925 
926 void m_redraw(int ev, object_t *op)
927 /* Redraw the screen. */
928 {
929 	object_t *op2;
930 
931 	if (ev != ctrl('L')) return;
932 
933 	clear_screen();
934 	for (op2= world; op2 != nil; op2= op2->next) op2->value[0]= 0;
935 }
936 
937 void m_toggle(int ev, object_t *op)
938 /* Toggle between the driver and alternate geometry. */
939 {
940 	unsigned t;
941 
942 	if (ev != 'X') return;
943 	if (alt_cyls == cylinders && alt_heads == heads && alt_secs == sectors)
944 		return;
945 
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();
951 }
952 
953 char size_last[]= "Size";
954 
955 void m_orientation(int ev, object_t *op)
956 {
957 	if (ev != ' ') return;
958 
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");
967 	}
968 }
969 
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.
973  */
974 {
975 	object_t *near, *op2;
976 	unsigned dist, d2, dr, dc;
977 
978 	if (ev != 'h' && ev != 'j' && ev != 'k' && ev != 'l' && ev != 'H')
979 		return;
980 
981 	if (device < 0) {
982 		/* No device open?  Then try to read first. */
983 		event('r', op);
984 		if (device < 0) return;
985 	}
986 
987 	near= op;
988 	dist= -1;
989 
990 	for (op2= world; op2 != nil; op2= op2->next) {
991 		if (op2 == op || !(op2->flags & OF_MOD)) continue;
992 
993 		dr= abs(op2->row - op->row);
994 		dc= abs(op2->col - op->col);
995 
996 		d2= 25*dr*dr + dc*dc;
997 		if (op2->row != op->row && op2->col != op->col) d2+= 1000;
998 
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;
1014 		}
1015 		if (d2 < dist) { near= op2; dist= d2; }
1016 	}
1017 	if (near != op) event(E_LEAVE, op);
1018 	event(E_ENTER, near);
1019 }
1020 
1021 void m_updown(int ev, object_t *op)
1022 /* Move a partition table entry up or down. */
1023 {
1024 	int i, j;
1025 	struct part_entry tmp;
1026 	int tmpx;
1027 
1028 	if (ev != ctrl('K') && ev != ctrl('J')) return;
1029 	if (op->entry == nil) return;
1030 
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;
1038 	}
1039 
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);
1045 }
1046 
1047 void m_enter(int ev, object_t *op)
1048 /* We've moved onto this object. */
1049 {
1050 	if (ev != E_ENTER && ev != ' ' && ev != '<' && ev != '>' && ev != 'X')
1051 		return;
1052 	curobj= op;
1053 	typing= 0;
1054 	magic= 0;
1055 }
1056 
1057 void m_leave(int ev, object_t *op)
1058 /* About to leave this object. */
1059 {
1060 	if (ev != E_LEAVE) return;
1061 }
1062 
1063 int within(unsigned *var, unsigned low, unsigned value, unsigned high)
1064 /* Only set *var to value if it looks reasonable. */
1065 {
1066 	if (low <= value && value <= high) {
1067 		*var= value;
1068 		return 1;
1069 	} else
1070 		return 0;
1071 }
1072 
1073 int lwithin(unsigned long *var, unsigned long low, unsigned long value,
1074 							unsigned long high)
1075 {
1076 	if (low <= value && value <= high) {
1077 		*var= value;
1078 		return 1;
1079 	} else
1080 		return 0;
1081 }
1082 
1083 int nextdevice(object_t *op, int delta)
1084 /* Select the next or previous device from the device list. */
1085 {
1086 	dev_t rdev;
1087 
1088 	if (offset != 0) return 0;
1089 	if (dirty) event(E_WRITE, op);
1090 	if (dirty) return 0;
1091 
1092 	if (device >= 0) {
1093 		(void) close(device);
1094 		device= -1;
1095 	}
1096 	recompute0();
1097 
1098 	rdev= curdev->rdev;
1099 	if (delta < 0) {
1100 		do
1101 			curdev= curdev->prev;
1102 		while (delta < -1 && major(curdev->rdev) == major(rdev)
1103 			&& curdev->rdev < rdev);
1104 	} else {
1105 		do
1106 			curdev= curdev->next;
1107 		while (delta > 1 && major(curdev->rdev) == major(rdev)
1108 			&& curdev->rdev > rdev);
1109 	}
1110 	return 1;
1111 }
1112 
1113 void check_ind(struct part_entry *pe)
1114 /* If there are no other partitions then make this new one active. */
1115 {
1116 	struct part_entry *pe2;
1117 
1118 	if (pe->sysind != NO_PART) return;
1119 
1120 	for (pe2= table + 1; pe2 < table + 1 + NR_PARTITIONS; pe2++)
1121 		if (pe2->sysind != NO_PART || pe2->bootind & ACTIVE_FLAG) break;
1122 
1123 	if (pe2 == table + 1 + NR_PARTITIONS) pe->bootind= ACTIVE_FLAG;
1124 }
1125 
1126 int check_existing(struct part_entry *pe)
1127 /* Check and if not ask if an existing partition may be modified. */
1128 {
1129 	static int expert= 0;
1130 	int c;
1131 
1132 	if (expert || pe == nil || !existing[pe - table]) return 1;
1133 
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'));
1141 }
1142 
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.
1146  */
1147 {
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;
1155 
1156 	if (device < 0 && op->type != O_DEV) return;
1157 
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;
1183 
1184 		mul= typing ? radix*radix : 0;
1185 		typing= 1;
1186 	}
1187 	magic= 0;
1188 
1189 	if (!check_existing(pe)) return;
1190 
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;
1216 		}
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;
1253 
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;
1276 			}
1277 			memset(pe, 0, sizeof(*pe));
1278 		}
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;
1292 	}
1293 
1294 	/* The order among the entries may have changed. */
1295 	sort();
1296 	dirty= 1;
1297 }
1298 
1299 unsigned long spell[3 + 4 * (1+NR_PARTITIONS)];
1300 int nspells;
1301 objtype_t touching;
1302 
1303 void newspell(unsigned long charm)
1304 /* Add a new spell, descending order for the base, ascending for the size. */
1305 {
1306 	int i, j;
1307 
1308 	if (charm - table[0].lowsec > table[0].size) return;
1309 
1310 	for (i= 0; i < nspells; i++) {
1311 		if (charm == spell[i]) return;	/* duplicate */
1312 
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;
1319 		}
1320 	}
1321 	for (j= ++nspells; j > i; j--) spell[j]= spell[j-1];
1322 	spell[i]= charm;
1323 }
1324 
1325 void m_magic(int ev, object_t *op)
1326 /* Apply magic onto a base or size number. */
1327 {
1328 	struct part_entry *pe= op->entry, *pe2;
1329 	int rough= (offset != 0 && extbase == 0);
1330 
1331 	if (ev != 'm' || device < 0) return;
1332 	typing= 0;
1333 
1334 	if (!check_existing(pe)) return;
1335 
1336 	if (magic == 0) {
1337 		/* See what magic we can let loose on this value. */
1338 		nspells= 1;
1339 
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;
1359 		}
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;
1368 
1369 				for (op2= world; op2 != nil; op2= op2->next) {
1370 					if (op2->row == op->row &&
1371 							op2->type == O_BASE) {
1372 						event('m', op2);
1373 					}
1374 				}
1375 				magic= 0;
1376 				event('m', op);
1377 				return;
1378 			}
1379 		}
1380 		/* Avoid the first sector on the device. */
1381 		if (spell[0] == table[0].lowsec) newspell(spell[0] + 1);
1382 
1383 		/* Further interesting values are the the bases of other
1384 		 * partitions or their ends.
1385 		 */
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);
1396 			}
1397 			if (pe2->lowsec % sectors != 0) rough= 1;
1398 		}
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.
1403 		 */
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);
1413 		}
1414 	}
1415 	/* Magic has been applied, a spell needs to be chosen. */
1416 
1417 	if (++magic == nspells) magic= 0;
1418 
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;
1424 
1425 	/* The order among the entries may have changed. */
1426 	sort();
1427 	dirty= 1;
1428 }
1429 
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;
1438 
1439 diving_t *diving= nil;
1440 
1441 void m_in(int ev, object_t *op)
1442 /* Go down into a primary or extended partition. */
1443 {
1444 	diving_t *newdiv;
1445 	struct part_entry *pe= op->entry, ext;
1446 	int n;
1447 
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;
1452 
1453 	ext= *pe;
1454 	if (extbase != 0) ext.size= extbase + extsize - ext.lowsec;
1455 
1456 	if (dirty) event(E_WRITE, op);
1457 	if (dirty) return;
1458 	if (device >= 0) { close(device); device= -1; }
1459 
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;
1468 
1469 	table[0]= ext;
1470 
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;
1477 
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;
1484 	}
1485 
1486 	submerged= 1;
1487 	event('r', op);
1488 }
1489 
1490 void m_out(int ev, object_t *op)
1491 /* Go up from an extended or subpartition table to its enclosing. */
1492 {
1493 	diving_t *olddiv;
1494 
1495 	if (ev != '<' || diving == nil) return;
1496 
1497 	if (dirty) event(E_WRITE, op);
1498 	if (dirty) return;
1499 	if (device >= 0) { close(device); device= -1; }
1500 
1501 	olddiv= diving;
1502 	diving= olddiv->up;
1503 
1504 	table[0]= olddiv->old0;
1505 
1506 	free(curdev->subname);
1507 	curdev->subname= olddiv->oldsubname;
1508 
1509 	curdev->parttype= olddiv->oldparttype;
1510 	offset= olddiv->oldoffset;
1511 	extbase= olddiv->oldextbase;
1512 
1513 	free(olddiv);
1514 
1515 	event('r', op);
1516 	if (diving == nil) submerged= 0;	/* We surfaced. */
1517 }
1518 
1519 void installboot(unsigned char *bootblock, char *masterboot)
1520 /* Install code from a master bootstrap into a boot block. */
1521 {
1522 	FILE *mfp;
1523 	unsigned char buf[SECTOR_SIZE];
1524 	int n;
1525 	char *err;
1526 
1527 	if ((mfp= fopen(masterboot, "r")) == nil) {
1528 		err= strerror(errno);
1529 		goto m_err;
1530 	}
1531 
1532 	n= fread(buf, sizeof(char), SECTOR_SIZE, mfp);
1533 	if (ferror(mfp)) {
1534 		err= strerror(errno);
1535 		fclose(mfp);
1536 		goto m_err;
1537 	}
1538 	else if (n < 256) {
1539 		err= "Is probably not a boot sector, too small";
1540 		fclose(mfp);
1541 		goto m_err;
1542 	}
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;
1548 	}
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;
1554 		}
1555 		n = PART_TABLE_OFF;
1556 	}
1557 
1558 	if (n > PART_TABLE_OFF) {
1559 		err= "Does not fit in a boot sector";
1560 		fclose(mfp);
1561 		goto m_err;
1562 	}
1563 
1564 	memcpy(bootblock, buf, n);
1565 	fclose(mfp);
1566 
1567 	/* Bootstrap installed. */
1568 	return;
1569 
1570     m_err:
1571 	stat_start(1);
1572 	printf("%s: %s", masterboot, err);
1573 	stat_end(5);
1574 }
1575 
1576 ssize_t boot_readwrite(int rw)
1577 /* Read (0) or write (1) the boot sector. */
1578 {
1579 	int r = 0;
1580 
1581 	if (lseek(device, offset * SECTOR_SIZE, SEEK_SET) < 0)
1582 		return -1;
1583 
1584 	switch (rw) {
1585 	case 0:	r= read(device, bootblock, SECTOR_SIZE);	break;
1586 	case 1:	r= write(device, bootblock, SECTOR_SIZE);	break;
1587 	}
1588 
1589 	return r;
1590 }
1591 
1592 void m_read(int ev, object_t *op)
1593 /* Read the partition table from the current device. */
1594 {
1595 	int i, mode, n;
1596 	struct part_entry *pe;
1597 
1598 	if (ev != 'r' || device >= 0) return;
1599 
1600 	/* Open() may cause kernel messages: */
1601 	stat_start(0);
1602 	fflush(stdout);
1603 
1604 	if (((device= open(curdev->name, mode= O_RDWR, 0666)) < 0
1605 		&& (errno != EACCES
1606 			|| (device= open(curdev->name, mode= O_RDONLY)) < 0))
1607 	) {
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;
1613 	}
1614 
1615 	/* Assume up to five lines of kernel messages. */
1616 	statusrow+= 5-1;
1617 	stat_end(5);
1618 
1619 	if (mode == O_RDONLY) {
1620 		stat_start(1);
1621 		printf("%s: Readonly", curdev->name);
1622 		stat_end(5);
1623 	}
1624 	memset(bootblock, 0, sizeof(bootblock));
1625 
1626 	n= boot_readwrite(0);
1627 
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);
1636 
1637 	if (n < SECTOR_SIZE) n= SECTOR_SIZE;
1638 
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;
1643 	}
1644 	if (i <= NR_PARTITIONS || bootblock[510] != 0x55
1645 				|| bootblock[511] != 0xAA) {
1646 		/* Invalid boot block, install bootstrap, wipe partition table.
1647 		 */
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);
1654 	}
1655 
1656 	/* Fix an extended partition table up to something mere mortals can
1657 	 * understand.  Record already defined partitions.
1658 	 */
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;
1664 	}
1665 	geometry();
1666 	dirty= 0;
1667 
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);
1673 	}
1674 }
1675 
1676 void m_write(int ev, object_t *op)
1677 /* Write the partition table back if modified. */
1678 {
1679 	int c;
1680 	struct part_entry new_table[NR_PARTITIONS], *pe;
1681 
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);
1690 		}
1691 		return;
1692 	}
1693 
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);
1700 	}
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);
1706 	}
1707 	stat_start(1);
1708 	putstr("Save partition table? (y/n) ");
1709 	fflush(stdout);
1710 
1711 	while ((c= getchar()) != 'y' && c != 'n' && c != ctrl('?')) {}
1712 
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;
1717 
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);
1725 
1726 			/* Fear and loathing time: */
1727 			if (extbase != 0)
1728 				pe->lowsec-= ext_part(pe->sysind)
1729 						? extbase : offset;
1730 		}
1731 	}
1732 	memcpy(bootblock+PART_TABLE_OFF, new_table, sizeof(new_table));
1733 	bootblock[510]= 0x55;
1734 	bootblock[511]= 0xAA;
1735 
1736 	if (boot_readwrite(1) < 0) {
1737 		stat_start(1);
1738 		printf("%s: %s", curdev->name, strerror(errno));
1739 		stat_end(5);
1740 		return;
1741 	}
1742 	dirty= 0;
1743 }
1744 
1745 void m_shell(int ev, object_t *op)
1746 /* Shell escape, to do calculations for instance. */
1747 {
1748 	int r, pid, status;
1749 	void (*sigint)(int), (*sigquit)(int), (*sigterm)(int);
1750 
1751 	if (ev != 's') return;
1752 
1753 	reset_tty();
1754 	fflush(stdout);
1755 
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);
1770 	}
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)
1780 		;
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);
1786 }
1787 
1788 void m_dump(int ev, object_t *op)
1789 /* Raw dump of the partition table. */
1790 {
1791 	struct part_entry table[NR_PARTITIONS], *pe;
1792 	int i;
1793 	unsigned chs[3];
1794 
1795 	if (ev != 'p' || device < 0) return;
1796 
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);
1815 	}
1816 	stat_start(0);
1817 	printf("(Raw dump of the original %.40s table)",
1818 		curdev->subname);
1819 	stat_end(10);
1820 }
1821 
1822 int quitting= 0;
1823 
1824 void m_quit(int ev, object_t *op)
1825 /* Write the partition table if modified and exit. */
1826 {
1827 	if (ev != 'q' && ev != 'x') return;
1828 
1829 	quitting= 1;
1830 
1831 	if (dirty) event(E_WRITE, op);
1832 	if (dirty) quitting= 0;
1833 }
1834 
1835 void m_help(int ev, object_t *op)
1836 /* For people without a clue; let's hope they can find the '?' key. */
1837 {
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\"" },
1855 	};
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 '?'.",
1870 	};
1871 
1872 	if (ev == '?') {
1873 		struct help *hp;
1874 
1875 		for (hp= help; hp < arraylimit(help); hp++) {
1876 			stat_start(0);
1877 			printf("%-25s - %s", hp->keys, hp->what);
1878 			stat_end(0);
1879 		}
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;
1890 
1891 		for (ap= advice; ap < arraylimit(advice); ap++) {
1892 			stat_start(0);
1893 			putstr(*ap);
1894 			stat_end(0);
1895 		}
1896 	}
1897 }
1898 
1899 void event(int ev, object_t *op)
1900 /* Simply call all modifiers for an event, each one knows when to act. */
1901 {
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);
1919 }
1920 
1921 int keypress(void)
1922 /* Get a single keypress.  Translate compound keypresses (arrow keys) to
1923  * their simpler equivalents.
1924  */
1925 {
1926 	char ch;
1927 	int c;
1928 	int esc= 0;
1929 
1930 	set_cursor(curobj->row, curobj->col);
1931 	fflush(stdout);
1932 
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;
1942 			}
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;
1958 			}
1959 			/*FALL THROUGH*/
1960 		default:
1961 			esc= 0;
1962 		}
1963 	} while (esc > 0);
1964 
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;
1970 	}
1971 
1972 	return c;
1973 }
1974 
1975 void mainloop(void)
1976 /* Get keypress, handle event, display results, reset screen, ad infinitum. */
1977 {
1978 	int key;
1979 
1980 	while (!quitting) {
1981 		stat_reset();
1982 
1983 		key= keypress();
1984 
1985 		event(key, curobj);
1986 
1987 		display();
1988 	}
1989 }
1990 
1991 int main(int argc, char **argv)
1992 {
1993 	object_t *op;
1994 	int i, r, key;
1995 	struct part_entry *pe;
1996 
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";
2009 
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);
2013 
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];
2018 
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];
2029 
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;
2045 	}
2046 
2047 	for (i= 1; i < argc; i++) newdevice(argv[i], 0);
2048 
2049 	if (firstdev == nil) {
2050 		getdevices();
2051 		key= ctrl('L');
2052 	} else {
2053 		key= 'r';
2054 	}
2055 
2056 	if (firstdev != nil) {
2057 		init_tty();
2058 		clear_screen();
2059 		event(key, curobj);
2060 		display();
2061 		mainloop();
2062 		reset_tty();
2063 	}
2064 	exit(0);
2065 }
2066