xref: /minix/minix/commands/autopart/autopart.c (revision 83133719)
1 /*	part 1.57 - Partition table editor		Author: Kees J. Bot
2  *								13 Mar 1992
3  * Needs about 22k heap+stack.
4  *
5  * Forked july 2005 into autopart (Ben Gras), a mode which gives the user
6  * an easier time.
7  *
8  */
9 #define nil 0
10 #include <sys/types.h>
11 #include <stdio.h>
12 #include <termcap.h>
13 #include <errno.h>
14 #include <unistd.h>
15 #include <stddef.h>
16 #include <ctype.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #include <signal.h>
20 #include <fcntl.h>
21 #include <time.h>
22 #include <dirent.h>
23 #include <limits.h>
24 #include <sys/stat.h>
25 #include <sys/wait.h>
26 #include <sys/ioctl.h>
27 #include <minix/config.h>
28 #include <minix/const.h>
29 #include <minix/partition.h>
30 #include <minix/com.h>
31 #include <machine/partition.h>
32 #include <termios.h>
33 #include <stdarg.h>
34 
35 /* Declare prototype. */
36 void printstep(int step, char *message);
37 
38 /* True if a partition is an extended partition. */
39 #define ext_part(s)	((s) == 0x05 || (s) == 0x0F)
40 
41 /* Minix master bootstrap code. */
42 char MASTERBOOT[] = "/usr/mdec/mbr";
43 
44 /* Template:
45                       ----first----  --geom/last--  ------sectors-----
46     Device             Cyl Head Sec   Cyl Head Sec      Base      Size        Kb
47     /dev/c0d0                          977    5  17
48     /dev/c0d0:2          0    0   2   976    4  16         2     83043     41521
49 Num Sort   Type
50  0* p0   81 MINIX        0    0   3    33    4   9         3      2880      1440
51  1  p1   81 MINIX       33    4  10   178    2   2      2883     12284      6142
52  2  p2   81 MINIX      178    2   3   976    4  16     15167     67878     33939
53  3  p3   00 None         0    0   0     0    0  -1         0         0         0
54 
55  */
56 #define MAXSIZE		999999999L
57 #define SECTOR_SIZE	512
58 #define DEV_FD0		0x200		/* Device number of /dev/fd0 */
59 #define DEV_C0D0	0x300		/* Device number of /dev/c0d0 */
60 
61 int min_region_mb = 500;
62 
63 #define MIN_REGION_SECTORS (1024*1024*min_region_mb/SECTOR_SIZE)
64 
65 #define arraysize(a)	(sizeof(a) / sizeof((a)[0]))
66 #define arraylimit(a)	((a) + arraysize(a))
67 
68 #define SORNOT(n) ((n) == 1 ? "" : "s")
69 
70 /* screen colours */
71 #define COL_RED		1
72 #define COL_GREEN	2
73 #define COL_ORANGE	3
74 #define COL_BLUE	4
75 #define COL_MAGENTA	5
76 #define COL_CYAN	6
77 
78 #define SURE_SERIOUS	1
79 #define SURE_BACK	2
80 
81 void col(int col)
82 {
83 	if(!col) printf("\033[0m");
84 	else printf("\033[3%dm", col % 10);
85 }
86 
87 void type2col(int type)
88 {
89 	switch(type) {
90 		/* minix */
91 		case 0x80:
92 		case MINIX_PART:	col(COL_GREEN);	break;
93 
94 		/* dos/windows */
95 		case 0x0B: case 0x0C: case 0x0E: case 0x0F: case 0x42:
96 		case 0x07: 		col(COL_CYAN);		break;
97 
98 		/* linux */
99 		case 0x82: case 0x83:	col(COL_ORANGE);	break;
100 	}
101 }
102 
103 int open_ct_ok(int fd)
104 {
105 	int c = -1;
106 	if(ioctl(fd, DIOCOPENCT, &c) < 0) {
107 		printf("Warning: couldn't verify opencount, continuing\n");
108 		return 1;
109 	}
110 
111 	if(c == 1) return 1;
112 	if(c < 1) { printf("Error: open count %d\n", c); }
113 
114 	return 0;
115 }
116 
117 void report(const char *label)
118 {
119 	fprintf(stderr, "part: %s: %s\n", label, strerror(errno));
120 }
121 
122 void fatal(const char *label)
123 {
124 	report(label);
125 	exit(1);
126 }
127 
128 struct termios termios;
129 
130 void restore_ttyflags(void)
131 /* Reset the tty flags to how we got 'em. */
132 {
133 	if (tcsetattr(0, TCSANOW, &termios) < 0) fatal("");
134 }
135 
136 void tty_raw(void)
137 /* Set the terminal to raw mode, no signals, no echoing. */
138 {
139 	struct termios rawterm;
140 
141 	rawterm= termios;
142 	rawterm.c_lflag &= ~(ICANON|ISIG|ECHO);
143 	rawterm.c_iflag &= ~(ICRNL);
144 	if (tcsetattr(0, TCSANOW, &rawterm) < 0) fatal("");
145 }
146 
147 #define ctrl(c)		((c) == '?' ? '\177' : ((c) & '\37'))
148 
149 char t_cd[16], t_cm[32], t_so[16], t_se[16], t_md[16], t_me[16];
150 #define STATUSROW	10
151 
152 int putchr(int c)
153 {
154 	return putchar(c);
155 }
156 
157 void putstr(char *s)
158 {
159 	int c;
160 
161 	while ((c= *s++) != 0) putchr(c);
162 }
163 
164 void set_cursor(int row, int col)
165 {
166 	tputs(tgoto(t_cm, col, row), 1, putchr);
167 }
168 
169 int statusrow= STATUSROW;
170 int stat_ktl= 1;
171 int need_help= 1;
172 
173 void stat_start(int serious)
174 /* Prepare for printing on a fresh status line, possibly highlighted. */
175 {
176 	set_cursor(statusrow++, 0);
177 	tputs(t_cd, 1, putchr);
178 	if (serious) tputs(t_so, 1, putchr);
179 }
180 
181 void stat_end(int ktl)
182 /* Closing bracket for stat_start.  Sets "keystrokes to live" of message. */
183 {
184 	tputs(t_se, 1, putchr);
185 	stat_ktl= ktl;
186 	need_help= 1;
187 }
188 
189 void stat_reset(void)
190 /* Reset the statusline pointer and clear old messages if expired. */
191 {
192 	if (stat_ktl > 0 && --stat_ktl == 0) {
193 		statusrow= STATUSROW;
194 		need_help= 1;
195 	}
196 	if (need_help && statusrow < (24-2)) {
197 		if (statusrow > STATUSROW) stat_start(0);
198 		stat_start(0);
199 		putstr(
200 "Type '+' or '-' to change, 'r' to read, '?' for more help, '!' for advice");
201 	}
202 	statusrow= STATUSROW;
203 	need_help= 0;
204 }
205 
206 void clear_screen(void)
207 {
208 	set_cursor(0, 0);
209 	tputs(t_cd, 1, putchr);
210 	stat_ktl= 1;
211 	stat_reset();
212 }
213 
214 void reset_tty(void)
215 /* Reset the tty to cooked mode. */
216 {
217 	restore_ttyflags();
218 	set_cursor(statusrow, 0);
219 	tputs(t_cd, 1, putchr);
220 }
221 
222 void *alloc(size_t n)
223 {
224 	void *m;
225 
226 	if ((m= malloc(n)) == nil) { reset_tty(); fatal(""); }
227 
228 	return m;
229 }
230 
231 typedef enum parttype { DUNNO, SUBPART, PRIMARY, FLOPPY } parttype_t;
232 
233 typedef struct device {
234 	struct device *next, *prev;	/* Circular dequeue. */
235 	dev_t	rdev;			/* Device number (sorting only). */
236 	char	*name;			/* E.g. /dev/c0d0 */
237 	char	*subname;		/* E.g. /dev/c0d0:2 */
238 	parttype_t parttype;
239 	int biosdrive;
240 } device_t;
241 
242 typedef struct region {
243 	/* A region is either an existing top-level partition
244 	 * entry (used_part is non-NULL) or free space (free_*
245 	 * contains data).
246 	 */
247 	struct part_entry used_part;
248 	int is_used_part;
249 	int tableno;
250 	int free_sec_start, free_sec_last;
251 } region_t;
252 
253 /* A disk has between 1 and 2*partitions+1 regions;
254  * the last case is free space before and after every partition.
255  */
256 #define NR_REGIONS (2*NR_PARTITIONS+1)
257 region_t regions[NR_REGIONS];
258 int nr_partitions = 0, nr_regions = 0, free_regions, used_regions;
259 int nordonly = 0;
260 
261 device_t *firstdev= nil, *curdev;
262 
263 #define MAX_DEVICES 100
264 	static struct {
265 		device_t *dev;
266 		int nr_partitions, free_regions, used_regions, sectors, nr_regions;
267 		int biosdrive;
268 		region_t regions[NR_REGIONS];
269 	} devices[MAX_DEVICES];
270 
271 void newdevice(char *name, int scanning, int disk_only)
272 /* Add a device to the device list.  If scanning is set then we are reading
273  * /dev, so insert the device in device number order and make /dev/c0d0 current.
274  */
275 {
276 	device_t *new, *nextdev, *prevdev;
277 	struct stat st;
278 
279 	st.st_rdev= 0;
280 	if (scanning) {
281 		if (stat(name, &st) < 0 || !S_ISBLK(st.st_mode)) return;
282 
283 		switch (major(st.st_rdev)) {
284 		case 3:
285 			/* Disk controller */
286 			if (minor(st.st_rdev) >= 0x80
287 					|| minor(st.st_rdev) % 5 != 0) return;
288 			break;
289 		default:
290 			return;
291 		}
292 		/* Interesting device found. */
293 	} else {
294 		if(stat(name, &st) < 0) { perror(name); return; }
295 	}
296 
297 	new= alloc(sizeof(*new));
298 	new->rdev= st.st_rdev;
299 	new->name= alloc((strlen(name) + 1) * sizeof(new->name[0]));
300 	strcpy(new->name, name);
301 	new->subname= new->name;
302 	new->parttype= DUNNO;
303 	if (major(st.st_rdev) == major(DEV_FD0) && minor(st.st_rdev) < 112) {
304 		new->parttype= FLOPPY;
305 	} else
306 	if (st.st_rdev >= DEV_C0D0 && minor(st.st_rdev) < 128
307 			&& minor(st.st_rdev) % 5 == 0) {
308 		new->parttype= PRIMARY;
309 	}
310 
311 	if (firstdev == nil) {
312 		firstdev= new;
313 		new->next= new->prev= new;
314 		curdev= firstdev;
315 		return;
316 	}
317 	nextdev= firstdev;
318 	while (new->rdev >= nextdev->rdev
319 				&& (nextdev= nextdev->next) != firstdev) {}
320 	prevdev= nextdev->prev;
321 	new->next= nextdev;
322 	nextdev->prev= new;
323 	new->prev= prevdev;
324 	prevdev->next= new;
325 
326 	if (new->rdev < firstdev->rdev) firstdev= new;
327 	if (new->rdev == DEV_C0D0) curdev= new;
328 	if (curdev->rdev != DEV_C0D0) curdev= firstdev;
329 }
330 
331 void getdevices(void)
332 /* Get all block devices from /dev that look interesting. */
333 {
334 	DIR *d;
335 	struct dirent *e;
336 	char name[5 + NAME_MAX + 1];
337 
338 	if ((d= opendir("/dev")) == nil) fatal("/dev");
339 
340 	while ((e= readdir(d)) != nil) {
341 		strcpy(name, "/dev/");
342 		strcpy(name + 5, e->d_name);
343 		newdevice(name, 1, 1);
344 	}
345 	(void) closedir(d);
346 }
347 
348 int dirty= 0;
349 unsigned char bootblock[SECTOR_SIZE];
350 struct part_entry table[1 + NR_PARTITIONS];
351 int existing[1 + NR_PARTITIONS];
352 unsigned long offset= 0, extbase= 0, extsize;
353 int submerged= 0;
354 int sort_index[1 + NR_PARTITIONS], sort_order[1 + NR_PARTITIONS];
355 unsigned cylinders= 1, heads= 1, sectors= 1, secpcyl= 1;
356 unsigned alt_cyls= 1, alt_heads= 1, alt_secs= 1;
357 int precise= 0;
358 int device= -1;
359 
360 unsigned long sortbase(struct part_entry *pe)
361 {
362 	return pe->sysind == NO_PART ? -1 : pe->lowsec;
363 }
364 
365 void sort(void)
366 /* Let the sort_index array show the order partitions are sorted in. */
367 {
368 	int i, j;
369 
370 	for (i= 1; i <= NR_PARTITIONS; i++) sort_order[i]= i;
371 
372 	for (i= 1; i <= NR_PARTITIONS; i++) {
373 		for (j= 1; j <= NR_PARTITIONS-1; j++) {
374 			int sj= sort_order[j], sj1= sort_order[j+1];
375 
376 			if (sortbase(&table[sj]) > sortbase(&table[sj1])) {
377 				sort_order[j]= sj1;
378 				sort_order[j+1]= sj;
379 			}
380 		}
381 	}
382 	for (i= 1; i <= NR_PARTITIONS; i++) sort_index[sort_order[i]]= i;
383 }
384 
385 void dos2chs(unsigned char *dos, unsigned *chs)
386 /* Extract cylinder, head and sector from the three bytes DOS uses to address
387  * a sector.  Note that bits 8 & 9 of the cylinder number come from bit 6 & 7
388  * of the sector byte.  The sector number is rebased to count from 0.
389  */
390 {
391 	chs[0]= ((dos[1] & 0xC0) << 2) | dos[2];
392 	chs[1]= dos[0];
393 	chs[2]= (dos[1] & 0x3F) - 1;
394 }
395 
396 void abs2dos(unsigned char *dos, unsigned long pos)
397 /* Translate a sector offset to three DOS bytes. */
398 {
399 	unsigned h, c, s;
400 
401 	c= pos / secpcyl;
402 	h= (pos % secpcyl) / sectors;
403 	s= pos % sectors + 1;
404 
405 	dos[0]= h;
406 	dos[1]= s | ((c >> 2) & 0xC0);
407 	dos[2]= c & 0xFF;
408 }
409 
410 void recompute0(void)
411 /* Recompute the partition size for the device after a geometry change. */
412 {
413 	if (device < 0) {
414 		cylinders= heads= sectors= 1;
415 		memset(table, 0, sizeof(table));
416 	} else
417 	if (!precise && offset == 0) {
418 		table[0].lowsec= 0;
419 		table[0].size= (unsigned long) cylinders * heads * sectors;
420 	}
421 	table[0].sysind= device < 0 ? NO_PART : MINIX_PART;
422 	secpcyl= heads * sectors;
423 }
424 
425 void guess_geometry(void)
426 /* With a bit of work one can deduce the disk geometry from the partition
427  * table.  This may be necessary if the driver gets it wrong.  (If partition
428  * tables didn't have C/H/S numbers we would not care at all...)
429  */
430 {
431 	int i, n;
432 	struct part_entry *pe;
433 	unsigned chs[3];
434 	unsigned long sec;
435 	unsigned h, s;
436 	unsigned char HS[256][8];	/* Bit map off all possible H/S */
437 
438 	alt_cyls= alt_heads= alt_secs= 0;
439 
440 	/* Initially all possible H/S combinations are possible.  HS[h][0]
441 	 * bit 0 is used to rule out a head value.
442 	 */
443 	for (h= 1; h <= 255; h++) {
444 		for (s= 0; s < 8; s++) HS[h][s]= 0xFF;
445 	}
446 
447 	for (i= 0; i < 2*NR_PARTITIONS; i++) {
448 		pe= &(table+1)[i >> 1];
449 		if (pe->sysind == NO_PART) continue;
450 
451 		/* Get the end or start sector numbers (in that order). */
452 		if ((i & 1) == 0) {
453 			dos2chs(&pe->last_head, chs);
454 			sec= pe->lowsec + pe->size - 1;
455 		} else {
456 			dos2chs(&pe->start_head, chs);
457 			sec= pe->lowsec;
458 		}
459 
460 		if (chs[0] >= alt_cyls) alt_cyls= chs[0]+1;
461 
462 		/* Which H/S combinations can be ruled out? */
463 		for (h= 1; h <= 255; h++) {
464 			if (HS[h][0] == 0) continue;
465 			n = 0;
466 			for (s= 1; s <= 63; s++) {
467 				if ((chs[0] * h + chs[1]) * s + chs[2] != sec) {
468 					HS[h][s/8] &= ~(1 << (s%8));
469 				}
470 				if (HS[h][s/8] & (1 << (s%8))) n++;
471 			}
472 			if (n == 0) HS[h][0]= 0;
473 		}
474 	}
475 
476 	/* See if only one remains. */
477 	i= 0;
478 	for (h= 1; h <= 255; h++) {
479 		if (HS[h][0] == 0) continue;
480 		for (s= 1; s <= 63; s++) {
481 			if (HS[h][s/8] & (1 << (s%8))) {
482 				i++;
483 				alt_heads= h;
484 				alt_secs= s;
485 			}
486 		}
487 	}
488 
489 	/* Forget it if more than one choice... */
490 	if (i > 1) alt_cyls= alt_heads= alt_secs= 0;
491 }
492 
493 void geometry(void)
494 /* Find out the geometry of the device by querying the driver, or by looking
495  * at the partition table.  These numbers are crosschecked to make sure that
496  * the geometry is correct.  Master bootstraps other than the Minix one use
497  * the CHS numbers in the partition table to load the bootstrap of the active
498  * partition.
499  */
500 {
501 	struct stat dst;
502 	int err= 0;
503 	struct part_geom geometry;
504 
505 	if (submerged) {
506 		/* Geometry already known. */
507 		sort();
508 		return;
509 	}
510 	precise= 0;
511 	cylinders= 0;
512 	recompute0();
513 	if (device < 0) return;
514 
515 	/* Try to guess the geometry from the partition table. */
516 	guess_geometry();
517 
518 	/* Try to get the geometry from the driver. */
519 	(void) fstat(device, &dst);
520 
521 	if (S_ISBLK(dst.st_mode) || S_ISCHR(dst.st_mode)) {
522 		/* Try to get the drive's geometry from the driver. */
523 
524 		if (ioctl(device, DIOCGETP, &geometry) < 0)
525 			err= errno;
526 		else {
527 			table[0].lowsec= (unsigned long)(geometry.base /
528 							SECTOR_SIZE);
529 			table[0].size  = (unsigned long)(geometry.size /
530 							SECTOR_SIZE);
531 			cylinders= geometry.cylinders;
532 			heads= geometry.heads;
533 			sectors= geometry.sectors;
534 			precise= 1;
535 		}
536 	} else {
537 		err= ENODEV;
538 	}
539 
540 	if (err != 0) {
541 		/* Getting the geometry from the driver failed, so use the
542 		 * alternate geometry.
543 		 */
544 		if (alt_heads == 0) {
545 			alt_cyls= table[0].size / (64 * 32);
546 			alt_heads= 64;
547 			alt_secs= 32;
548 		}
549 
550 		cylinders= alt_cyls;
551 		heads= alt_heads;
552 		sectors= alt_secs;
553 
554 		stat_start(1);
555 		printf("Failure to get the geometry of %s: %s", curdev->name,
556 			errno == ENOTTY ? "No driver support" : strerror(err));
557 		stat_end(5);
558 		stat_start(0);
559 		printf("The geometry has been guessed as %ux%ux%u",
560 						cylinders, heads, sectors);
561 		stat_end(5);
562 	} else {
563 		if (alt_heads == 0) {
564 			alt_cyls= cylinders;
565 			alt_heads= heads;
566 			alt_secs= sectors;
567 		}
568 
569 		if (heads != alt_heads || sectors != alt_secs) {
570 printf(
571 "The geometry obtained from the driver\n"
572 "does not match the geometry implied by the partition\n"
573 "table. Please use expert mode instead.\n");
574 exit(1);
575 		}
576 	}
577 
578 	/* Show the base and size of the device instead of the whole drive.
579 	 * This makes sense for subpartitioning primary partitions.
580 	 */
581 	if (precise && ioctl(device, DIOCGETP, &geometry) >= 0) {
582 		table[0].lowsec= (unsigned long)(geometry.base / SECTOR_SIZE);
583 		table[0].size  = (unsigned long)(geometry.size / SECTOR_SIZE);
584 	} else {
585 		precise= 0;
586 	}
587 	recompute0();
588 	sort();
589 }
590 
591 typedef struct indicators {	/* Partition type to partition name. */
592 	unsigned char	ind;
593 	char		name[10];
594 } indicators_t;
595 
596 indicators_t ind_table[]= {
597 	{ 0x00,		"None"		},
598 	{ 0x01,		"FAT-12"	},
599 	{ 0x02,		"XENIX /"	},
600 	{ 0x03,		"XENIX usr"	},
601 	{ 0x04,		"FAT-16"	},
602 	{ 0x05,		"EXTENDED"	},
603 	{ 0x06,		"FAT-16"	},
604 	{ 0x07,		"HPFS/NTFS"	},
605 	{ 0x08,		"AIX"		},
606 	{ 0x09,		"COHERENT"	},
607 	{ 0x0A,		"OS/2"		},
608 	{ 0x0B,		"FAT-32"	},
609 	{ 0x0C,		"FAT?"		},
610 	{ 0x0E,		"FAT?"		},
611 	{ 0x0F,		"EXTENDED"	},
612 	{ 0x10,		"OPUS"		},
613 	{ 0x40,		"VENIX286"	},
614 	{ 0x42,		"W2000 Dyn"	},
615 	{ 0x52,		"MICROPORT"	},
616 	{ 0x63,		"386/IX"	},
617 	{ 0x64,		"NOVELL286"	},
618 	{ 0x65,		"NOVELL386"	},
619 	{ 0x75,		"PC/IX"		},
620 	{ 0x80,		"MINIX-OLD"	},
621 	{ 0x81,		"MINIX"		},
622 	{ 0x82,		"LINUXswap"	},
623 	{ 0x83,		"LINUX"		},
624 	{ 0x93,		"AMOEBA"	},
625 	{ 0x94,		"AMOEBAbad"	},
626 	{ 0xA5,		"386BSD"	},
627 	{ 0xB7,		"BSDI"		},
628 	{ 0xB8,		"BSDI swap"	},
629 	{ 0xC7,		"SYRINX"	},
630 	{ 0xDB,		"CPM"		},
631 	{ 0xFF,		"BADBLOCKS"	},
632 };
633 
634 char *typ2txt(int ind)
635 /* Translate a numeric partition indicator for human eyes. */
636 {
637 	indicators_t *pind;
638 
639 	for (pind= ind_table; pind < arraylimit(ind_table); pind++) {
640 		if (pind->ind == ind) return pind->name;
641 	}
642 	return "unknown system";
643 }
644 
645 int round_sysind(int ind, int delta)
646 /* Find the next known partition type starting with ind in direction delta. */
647 {
648 	indicators_t *pind;
649 
650 	ind= (ind + delta) & 0xFF;
651 
652 	if (delta < 0) {
653 		for (pind= arraylimit(ind_table)-1; pind->ind > ind; pind--) {}
654 	} else {
655 		for (pind= ind_table; pind->ind < ind; pind++) {}
656 	}
657 	return pind->ind;
658 }
659 
660 /* Objects on the screen, either simple pieces of the text or the cylinder
661  * number of the start of partition three.
662  */
663 typedef enum objtype {
664 	O_INFO, O_TEXT, O_DEV, O_SUB,
665 	O_TYPTXT, O_SORT, O_NUM, O_TYPHEX,
666 	O_CYL, O_HEAD, O_SEC,
667 	O_SCYL, O_SHEAD, O_SSEC, O_LCYL, O_LHEAD, O_LSEC, O_BASE, O_SIZE, O_KB
668 } objtype_t;
669 
670 #define rjust(type)	((type) >= O_TYPHEX)
671 #define computed(type)	((type) >= O_TYPTXT)
672 
673 typedef struct object {
674 	struct object	*next;
675 	objtype_t	type;		/* Text field, cylinder number, etc. */
676 	char		flags;		/* Modifiable? */
677 	char		row;
678 	char		col;
679 	char		len;
680 	struct part_entry *entry;	/* What does the object refer to? */
681 	char		  *text;
682 	char		value[20];	/* Value when printed. */
683 } object_t;
684 
685 #define OF_MOD		0x01	/* Object value is modifiable. */
686 #define OF_ODD		0x02	/* It has a somewhat odd value. */
687 #define OF_BAD		0x04	/* Its value is no good at all. */
688 
689 /* Events: (Keypress events are the value of the key pressed.) */
690 #define E_ENTER		(-1)	/* Cursor moves onto object. */
691 #define E_LEAVE		(-2)	/* Cursor leaves object. */
692 #define E_WRITE		(-3)	/* Write, but not by typing 'w'. */
693 
694 /* The O_SIZE objects have a dual identity. */
695 enum howend { SIZE, LAST } howend= SIZE;
696 
697 object_t *world= nil;
698 object_t *curobj= nil;
699 
700 object_t *newobject(objtype_t type, int flags, int row, int col, int len)
701 /* Make a new object given a type, flags, position and length on the screen. */
702 {
703 	object_t *new;
704 	object_t **aop= &world;
705 
706 	new= alloc(sizeof(*new));
707 
708 	new->type= type;
709 	new->flags= flags;
710 	new->row= row;
711 	new->col= col;
712 	new->len= len;
713 	new->entry= nil;
714 	new->text= "";
715 	new->value[0]= 0;
716 
717 	new->next= *aop;
718 	*aop= new;
719 
720 	return new;
721 }
722 
723 unsigned long entry2base(struct part_entry *pe)
724 /* Return the base sector of the partition if defined. */
725 {
726 	return pe->sysind == NO_PART ? 0 : pe->lowsec;
727 }
728 
729 unsigned long entry2last(struct part_entry *pe)
730 {
731 	return pe->sysind == NO_PART ? -1 : pe->lowsec + pe->size - 1;
732 }
733 
734 unsigned long entry2size(struct part_entry *pe)
735 {
736 	return pe->sysind == NO_PART ? 0 : pe->size;
737 }
738 
739 int typing;	/* Set if a digit has been typed to set a value. */
740 int magic;	/* Changes when using the magic key. */
741 
742 void event(int ev, object_t *op);
743 
744 void m_redraw(int ev, object_t *op)
745 /* Redraw the screen. */
746 {
747 	object_t *op2;
748 
749 	if (ev != ctrl('L')) return;
750 
751 	clear_screen();
752 	for (op2= world; op2 != nil; op2= op2->next) op2->value[0]= 0;
753 }
754 
755 void m_toggle(int ev, object_t *op)
756 /* Toggle between the driver and alternate geometry. */
757 {
758 	unsigned t;
759 
760 	if (ev != 'X') return;
761 	if (alt_cyls == cylinders && alt_heads == heads && alt_secs == sectors)
762 		return;
763 
764 	t= cylinders; cylinders= alt_cyls; alt_cyls= t;
765 	t= heads; heads= alt_heads; alt_heads= t;
766 	t= sectors; sectors= alt_secs; alt_secs= t;
767 	dirty= 1;
768 	recompute0();
769 }
770 
771 char size_last[]= "Size";
772 
773 void m_orientation(int ev, object_t *op)
774 {
775 	if (ev != ' ') return;
776 
777 	switch (howend) {
778 	case SIZE:
779 		howend= LAST;
780 		strcpy(size_last, "Last");
781 		break;
782 	case LAST:
783 		howend= SIZE;
784 		strcpy(size_last, "Size");
785 	}
786 }
787 
788 void m_move(int ev, object_t *op)
789 /* Move to the nearest modifiably object in the intended direction.  Objects
790  * on the same row or column are really near.
791  */
792 {
793 	object_t *near, *op2;
794 	unsigned dist, d2, dr, dc;
795 
796 	if (ev != 'h' && ev != 'j' && ev != 'k' && ev != 'l' && ev != 'H')
797 		return;
798 
799 	if (device < 0) {
800 		/* No device open?  Then try to read first. */
801 		event('r', op);
802 		if (device < 0) return;
803 	}
804 
805 	near= op;
806 	dist= -1;
807 
808 	for (op2= world; op2 != nil; op2= op2->next) {
809 		if (op2 == op || !(op2->flags & OF_MOD)) continue;
810 
811 		dr= abs(op2->row - op->row);
812 		dc= abs(op2->col - op->col);
813 
814 		d2= 25*dr*dr + dc*dc;
815 		if (op2->row != op->row && op2->col != op->col) d2+= 1000;
816 
817 		switch (ev) {
818 		case 'h':	/* Left */
819 			if (op2->col >= op->col) d2= -1;
820 			break;
821 		case 'j':	/* Down */
822 			if (op2->row <= op->row) d2= -1;
823 			break;
824 		case 'k':	/* Up */
825 			if (op2->row >= op->row) d2= -1;
826 			break;
827 		case 'l':	/* Right */
828 			if (op2->col <= op->col) d2= -1;
829 			break;
830 		case 'H':	/* Home */
831 			if (op2->type == O_DEV) d2= 0;
832 		}
833 		if (d2 < dist) { near= op2; dist= d2; }
834 	}
835 	if (near != op) event(E_LEAVE, op);
836 	event(E_ENTER, near);
837 }
838 
839 void m_updown(int ev, object_t *op)
840 /* Move a partition table entry up or down. */
841 {
842 	int i, j;
843 	struct part_entry tmp;
844 	int tmpx;
845 
846 	if (ev != ctrl('K') && ev != ctrl('J')) return;
847 	if (op->entry == nil) return;
848 
849 	i= op->entry - table;
850 	if (ev == ctrl('K')) {
851 		if (i <= 1) return;
852 		j= i-1;
853 	} else {
854 		if (i >= NR_PARTITIONS) return;
855 		j= i+1;
856 	}
857 
858 	tmp= table[i]; table[i]= table[j]; table[j]= tmp;
859 	tmpx= existing[i]; existing[i]= existing[j]; existing[j]= tmpx;
860 	sort();
861 	dirty= 1;
862 	event(ev == ctrl('K') ? 'k' : 'j', op);
863 }
864 
865 void m_enter(int ev, object_t *op)
866 /* We've moved onto this object. */
867 {
868 	if (ev != E_ENTER && ev != ' ' && ev != '<' && ev != '>' && ev != 'X')
869 		return;
870 	curobj= op;
871 	typing= 0;
872 	magic= 0;
873 }
874 
875 void m_leave(int ev, object_t *op)
876 /* About to leave this object. */
877 {
878 	if (ev != E_LEAVE) return;
879 }
880 
881 int within(unsigned *var, unsigned low, unsigned value, unsigned high)
882 /* Only set *var to value if it looks reasonable. */
883 {
884 	if (low <= value && value <= high) {
885 		*var= value;
886 		return 1;
887 	} else
888 		return 0;
889 }
890 
891 int lwithin(unsigned long *var, unsigned long low, unsigned long value,
892 							unsigned long high)
893 {
894 	if (low <= value && value <= high) {
895 		*var= value;
896 		return 1;
897 	} else
898 		return 0;
899 }
900 
901 int nextdevice(object_t *op, int delta)
902 /* Select the next or previous device from the device list. */
903 {
904 	dev_t rdev;
905 
906 	if (offset != 0) return 0;
907 	if (dirty) event(E_WRITE, op);
908 	if (dirty) return 0;
909 
910 	if (device >= 0) {
911 		(void) close(device);
912 		device= -1;
913 	}
914 	recompute0();
915 
916 	rdev= curdev->rdev;
917 	if (delta < 0) {
918 		do
919 			curdev= curdev->prev;
920 		while (delta < -1 && major(curdev->rdev) == major(rdev)
921 			&& curdev->rdev < rdev);
922 	} else {
923 		do
924 			curdev= curdev->next;
925 		while (delta > 1 && major(curdev->rdev) == major(rdev)
926 			&& curdev->rdev > rdev);
927 	}
928 	return 1;
929 }
930 
931 void check_ind(struct part_entry *pe)
932 /* If there are no other partitions then make this new one active. */
933 {
934 	struct part_entry *pe2;
935 	int i = 0;
936 
937 	for (pe2= table + 1; pe2 < table + 1 + NR_PARTITIONS; pe2++, i++)
938 		if (pe2->sysind != NO_PART && (pe2->bootind & ACTIVE_FLAG))
939 			return;
940 
941 	pe->bootind= ACTIVE_FLAG;
942 	dirty = 1;
943 }
944 
945 int check_existing(struct part_entry *pe)
946 /* Check and if not ask if an existing partition may be modified. */
947 {
948 	static int expert= 0;
949 	int c;
950 
951 	if (expert || pe == nil || !existing[pe - table]) return 1;
952 
953 	stat_start(1);
954 	putstr("Do you wish to modify existing partitions? (y/n) ");
955 	fflush(stdout);
956 	while ((c= getchar()) != 'y' && c != 'n') {}
957 	putchr(c);
958 	stat_end(3);
959 	return (expert= (c == 'y'));
960 }
961 
962 void m_modify(int ev, object_t *op)
963 /* Increment, decrement, set, or toggle the value of an object, using
964  * arithmetic tricks the author doesn't understand either.
965  */
966 {
967 	object_t *op2;
968 	struct part_entry *pe= op->entry;
969 	int mul, delta;
970 	unsigned level= 1;
971 	unsigned long surplus;
972 	int radix= op->type == O_TYPHEX ? 0x10 : 10;
973 	unsigned long t;
974 
975 	if (device < 0 && op->type != O_DEV) return;
976 
977 	switch (ev) {
978 	case '-':
979 		mul= radix; delta= -1; typing= 0;
980 		break;
981 	case '+':
982 		mul= radix; delta= 1; typing= 0;
983 		break;
984 	case '\b':
985 		if (!typing) return;
986 		mul= 1; delta= 0;
987 		break;
988 	case '\r':
989 		typing= 0;
990 		return;
991 	default:
992 		if ('0' <= ev && ev <= '9')
993 			delta= ev - '0';
994 		else
995 		if (radix == 0x10 && 'a' <= ev && ev <= 'f')
996 			delta= ev - 'a' + 10;
997 		else
998 		if (radix == 0x10 && 'A' <= ev && ev <= 'F')
999 			delta= ev - 'A' + 10;
1000 		else
1001 			return;
1002 
1003 		mul= typing ? radix*radix : 0;
1004 		typing= 1;
1005 	}
1006 	magic= 0;
1007 
1008 	if (!check_existing(pe)) return;
1009 
1010 	switch (op->type) {
1011 	case O_DEV:
1012 		if (ev != '-' && ev != '+') return;
1013 		if (!nextdevice(op, delta)) return;
1014 		break;
1015 	case O_CYL:
1016 		if (!within(&cylinders, 1,
1017 			cylinders * mul / radix + delta, 1024)) return;
1018 		recompute0();
1019 		break;
1020 	case O_HEAD:
1021 		if (!within(&heads, 1, heads * mul / radix + delta, 255))
1022 			return;
1023 		recompute0();
1024 		break;
1025 	case O_SEC:
1026 		if (!within(&sectors, 1, sectors * mul / radix + delta, 63))
1027 			return;
1028 		recompute0();
1029 		break;
1030 	case O_NUM:
1031 		if (ev != '-' && ev != '+') return;
1032 		for (op2= world; op2 != nil; op2= op2->next) {
1033 			if (op2->type == O_NUM && ev == '+')
1034 				op2->entry->bootind= 0;
1035 		}
1036 		op->entry->bootind= ev == '+' ? ACTIVE_FLAG : 0;
1037 		break;
1038 	case O_TYPHEX:
1039 		check_ind(pe);
1040 		pe->sysind= pe->sysind * mul / radix + delta;
1041 		break;
1042 	case O_TYPTXT:
1043 		if (ev != '-' && ev != '+') return;
1044 		check_ind(pe);
1045 		pe->sysind= round_sysind(pe->sysind, delta);
1046 		break;
1047 	case O_SCYL:
1048 		level= heads;
1049 	case O_SHEAD:
1050 		level*= sectors;
1051 	case O_SSEC:
1052 		if (op->type != O_SCYL && ev != '-' && ev != '+') return;
1053 	case O_BASE:
1054 		if (pe->sysind == NO_PART) memset(pe, 0, sizeof(*pe));
1055 		t= pe->lowsec;
1056 		surplus= t % level;
1057 		if (!lwithin(&t, 0L,
1058 			(t / level * mul / radix + delta) * level + surplus,
1059 			MAXSIZE)) return;
1060 		if (howend == LAST || op->type != O_BASE)
1061 			pe->size-= t - pe->lowsec;
1062 		pe->lowsec= t;
1063 		check_ind(pe);
1064 		if (pe->sysind == NO_PART) pe->sysind= MINIX_PART;
1065 		break;
1066 	case O_LCYL:
1067 		level= heads;
1068 	case O_LHEAD:
1069 		level*= sectors;
1070 	case O_LSEC:
1071 		if (op->type != O_LCYL && ev != '-' && ev != '+') return;
1072 
1073 		if (pe->sysind == NO_PART) memset(pe, 0, sizeof(*pe));
1074 		t= pe->lowsec + pe->size - 1 + level;
1075 		surplus= t % level - mul / radix * level;
1076 		if (!lwithin(&t, 0L,
1077 			(t / level * mul / radix + delta) * level + surplus,
1078 			MAXSIZE)) return;
1079 		pe->size= t - pe->lowsec + 1;
1080 		check_ind(pe);
1081 		if (pe->sysind == NO_PART) pe->sysind= MINIX_PART;
1082 		break;
1083 	case O_KB:
1084 		level= 2;
1085 		if (mul == 0) pe->size= 0;	/* new value, no surplus */
1086 	case O_SIZE:
1087 		if (pe->sysind == NO_PART) {
1088 			if (op->type == O_KB || howend == SIZE) {
1089 				/* First let loose magic to set the base. */
1090 				event('m', op);
1091 				magic= 0;
1092 				pe->size= 0;
1093 				event(ev, op);
1094 				return;
1095 			}
1096 			memset(pe, 0, sizeof(*pe));
1097 		}
1098 		t= (op->type == O_KB || howend == SIZE) ? pe->size
1099 						: pe->lowsec + pe->size - 1;
1100 		surplus= t % level;
1101 		if (!lwithin(&t, 0L,
1102 			(t / level * mul / radix + delta) * level + surplus,
1103 			MAXSIZE)) return;
1104 		pe->size= (op->type == O_KB || howend == SIZE) ? t :
1105 							t - pe->lowsec + 1;
1106 		check_ind(pe);
1107 		if (pe->sysind == NO_PART) pe->sysind= MINIX_PART;
1108 		break;
1109 	default:
1110 		return;
1111 	}
1112 
1113 	/* The order among the entries may have changed. */
1114 	sort();
1115 	dirty= 1;
1116 }
1117 
1118 unsigned long spell[3 + 4 * (1+NR_PARTITIONS)];
1119 int nspells;
1120 objtype_t touching;
1121 
1122 void newspell(unsigned long charm)
1123 /* Add a new spell, descending order for the base, ascending for the size. */
1124 {
1125 	int i, j;
1126 
1127 	if (charm - table[0].lowsec > table[0].size) return;
1128 
1129 	for (i= 0; i < nspells; i++) {
1130 		if (charm == spell[i]) return;	/* duplicate */
1131 
1132 		if (touching == O_BASE) {
1133 			if (charm == table[0].lowsec + table[0].size) return;
1134 			if ((spell[0] - charm) < (spell[0] - spell[i])) break;
1135 		} else {
1136 			if (charm == table[0].lowsec) return;
1137 			if ((charm - spell[0]) < (spell[i] - spell[0])) break;
1138 		}
1139 	}
1140 	for (j= ++nspells; j > i; j--) spell[j]= spell[j-1];
1141 	spell[i]= charm;
1142 }
1143 
1144 void m_magic(int ev, object_t *op)
1145 /* Apply magic onto a base or size number. */
1146 {
1147 	struct part_entry *pe= op->entry, *pe2;
1148 	int rough= (offset != 0 && extbase == 0);
1149 
1150 	if (ev != 'm' || device < 0) return;
1151 	typing= 0;
1152 
1153 	if (!check_existing(pe)) return;
1154 
1155 	if (magic == 0) {
1156 		/* See what magic we can let loose on this value. */
1157 		nspells= 1;
1158 
1159 		/* First spell, the current value. */
1160 		switch (op->type) {
1161 		case O_SCYL:
1162 		case O_SHEAD:	/* Start of partition. */
1163 		case O_SSEC:
1164 		case O_BASE:
1165 			touching= O_BASE;
1166 			spell[0]= pe->lowsec;
1167 			break;
1168 		case O_LCYL:
1169 		case O_LHEAD:
1170 		case O_LSEC:	/* End of partition. */
1171 		case O_KB:
1172 		case O_SIZE:
1173 			touching= O_SIZE;
1174 			spell[0]= pe->lowsec + pe->size;
1175 			break;
1176 		default:
1177 			return;
1178 		}
1179 		if (pe->sysind == NO_PART) {
1180 			memset(pe, 0, sizeof(*pe));
1181 			check_ind(pe);
1182 			pe->sysind= MINIX_PART;
1183 			spell[0]= 0;
1184 			if (touching == O_SIZE) {
1185 				/* First let loose magic on the base. */
1186 				object_t *op2;
1187 
1188 				for (op2= world; op2 != nil; op2= op2->next) {
1189 					if (op2->row == op->row &&
1190 							op2->type == O_BASE) {
1191 						event('m', op2);
1192 					}
1193 				}
1194 				magic= 0;
1195 				event('m', op);
1196 				return;
1197 			}
1198 		}
1199 		/* Avoid the first sector on the device. */
1200 		if (spell[0] == table[0].lowsec) newspell(spell[0] + 1);
1201 
1202 		/* Further interesting values are the the bases of other
1203 		 * partitions or their ends.
1204 		 */
1205 		for (pe2= table; pe2 < table + 1 + NR_PARTITIONS; pe2++) {
1206 			if (pe2 == pe || pe2->sysind == NO_PART) continue;
1207 			if (pe2->lowsec == table[0].lowsec)
1208 				newspell(table[0].lowsec + 1);
1209 			else
1210 				newspell(pe2->lowsec);
1211 			newspell(pe2->lowsec + pe2->size);
1212 			if (touching == O_BASE && howend == SIZE) {
1213 				newspell(pe2->lowsec - pe->size);
1214 				newspell(pe2->lowsec + pe2->size - pe->size);
1215 			}
1216 			if (pe2->lowsec % sectors != 0) rough= 1;
1217 		}
1218 		/* Present values rounded up to the next cylinder unless
1219 		 * the table is already a mess.  Use "start + 1 track" instead
1220 		 * of "start + 1 cylinder".  Also add the end of the last
1221 		 * cylinder.
1222 		 */
1223 		if (!rough) {
1224 			unsigned long n= spell[0];
1225 			if (n == table[0].lowsec) n++;
1226 			n= (n + sectors - 1) / sectors * sectors;
1227 			if (n != table[0].lowsec + sectors)
1228 				n= (n + secpcyl - 1) / secpcyl * secpcyl;
1229 			newspell(n);
1230 			if (touching == O_SIZE)
1231 				newspell(table[0].size / secpcyl * secpcyl);
1232 		}
1233 	}
1234 	/* Magic has been applied, a spell needs to be chosen. */
1235 
1236 	if (++magic == nspells) magic= 0;
1237 
1238 	if (touching == O_BASE) {
1239 		if (howend == LAST) pe->size-= spell[magic] - pe->lowsec;
1240 		pe->lowsec= spell[magic];
1241 	} else
1242 		pe->size= spell[magic] - pe->lowsec;
1243 
1244 	/* The order among the entries may have changed. */
1245 	sort();
1246 	dirty= 1;
1247 }
1248 
1249 typedef struct diving {
1250 	struct diving	*up;
1251 	struct part_entry  old0;
1252 	char		*oldsubname;
1253 	parttype_t	oldparttype;
1254 	unsigned long	oldoffset;
1255 	unsigned long	oldextbase;
1256 } diving_t;
1257 
1258 diving_t *diving= nil;
1259 
1260 void m_in(int ev, object_t *op)
1261 /* Go down into a primary or extended partition. */
1262 {
1263 	diving_t *newdiv;
1264 	struct part_entry *pe= op->entry, ext;
1265 	int n;
1266 
1267 	if (ev != '>' || device < 0 || pe == nil || pe == &table[0]
1268 		|| (!(pe->sysind == MINIX_PART && offset == 0)
1269 					&& !ext_part(pe->sysind))
1270 		|| pe->size == 0) return;
1271 
1272 	ext= *pe;
1273 	if (extbase != 0) ext.size= extbase + extsize - ext.lowsec;
1274 
1275 	if (dirty) event(E_WRITE, op);
1276 	if (dirty) return;
1277 	if (device >= 0) { close(device); device= -1; }
1278 
1279 	newdiv= alloc(sizeof(*newdiv));
1280 	newdiv->old0= table[0];
1281 	newdiv->oldsubname= curdev->subname;
1282 	newdiv->oldparttype= curdev->parttype;
1283 	newdiv->oldoffset= offset;
1284 	newdiv->oldextbase= extbase;
1285 	newdiv->up= diving;
1286 	diving= newdiv;
1287 
1288 	table[0]= ext;
1289 
1290 	n= strlen(diving->oldsubname);
1291 	curdev->subname= alloc((n + 3) * sizeof(curdev->subname[0]));
1292 	strcpy(curdev->subname, diving->oldsubname);
1293 	curdev->subname[n++]= ':';
1294 	curdev->subname[n++]= '0' + (pe - table - 1);
1295 	curdev->subname[n]= 0;
1296 
1297 	curdev->parttype= curdev->parttype == PRIMARY ? SUBPART : DUNNO;
1298 	offset= ext.lowsec;
1299 	if (ext_part(ext.sysind) && extbase == 0) {
1300 		extbase= ext.lowsec;
1301 		extsize= ext.size;
1302 		curdev->parttype= DUNNO;
1303 	}
1304 
1305 	submerged= 1;
1306 	event('r', op);
1307 }
1308 
1309 void m_out(int ev, object_t *op)
1310 /* Go up from an extended or subpartition table to its enclosing. */
1311 {
1312 	diving_t *olddiv;
1313 
1314 	if (ev != '<' || diving == nil) return;
1315 
1316 	if (dirty) event(E_WRITE, op);
1317 	if (dirty) return;
1318 	if (device >= 0) { close(device); device= -1; }
1319 
1320 	olddiv= diving;
1321 	diving= olddiv->up;
1322 
1323 	table[0]= olddiv->old0;
1324 
1325 	free(curdev->subname);
1326 	curdev->subname= olddiv->oldsubname;
1327 
1328 	curdev->parttype= olddiv->oldparttype;
1329 	offset= olddiv->oldoffset;
1330 	extbase= olddiv->oldextbase;
1331 
1332 	free(olddiv);
1333 
1334 	event('r', op);
1335 	if (diving == nil) submerged= 0;	/* We surfaced. */
1336 }
1337 
1338 void installboot(unsigned char *bootblock, char *masterboot)
1339 /* Install code from a master bootstrap into a boot block. */
1340 {
1341 	FILE *mfp;
1342 	unsigned char buf[SECTOR_SIZE];
1343 	int n;
1344 	char *err;
1345 
1346 	if ((mfp= fopen(masterboot, "r")) == nil) {
1347 		err= strerror(errno);
1348 		goto m_err;
1349 	}
1350 
1351 	n= fread(buf, sizeof(char), SECTOR_SIZE, mfp);
1352 	if (ferror(mfp)) {
1353 		err= strerror(errno);
1354 		fclose(mfp);
1355 		goto m_err;
1356 	}
1357 	else if (n < 256) {
1358 		err= "Is probably not a boot sector, too small";
1359 		fclose(mfp);
1360 		goto m_err;
1361 	}
1362 	else if (n < SECTOR_SIZE && n > PART_TABLE_OFF) {
1363 		/* if only code, it cannot override partition table */
1364 		err= "Does not fit in a boot sector";
1365 		fclose(mfp);
1366 		goto m_err;
1367 	}
1368 	else if (n == SECTOR_SIZE) {
1369 		if (buf[510] != 0x55 || buf[511] != 0xaa) {
1370 			err= "Is not a boot sector (bad magic)";
1371 			fclose(mfp);
1372 			goto m_err;
1373 		}
1374 		n = PART_TABLE_OFF;
1375 	}
1376 
1377 	if (n > PART_TABLE_OFF) {
1378 		err= "Does not fit in a boot sector";
1379 		fclose(mfp);
1380 		goto m_err;
1381 	}
1382 
1383 	memcpy(bootblock, buf, n);
1384 	fclose(mfp);
1385 
1386 	/* Bootstrap installed. */
1387 	return;
1388 
1389     m_err:
1390 	stat_start(1);
1391 	printf("%s: %s", masterboot, err);
1392 	stat_end(5);
1393 }
1394 
1395 ssize_t boot_readwrite(int rw)
1396 /* Read (0) or write (1) the boot sector. */
1397 {
1398 	int r = 0;
1399 
1400 	if (lseek(device, offset * SECTOR_SIZE, SEEK_SET) < 0)
1401 		return -1;
1402 
1403 	switch (rw) {
1404 	case 0:	r= read(device, bootblock, SECTOR_SIZE);	break;
1405 	case 1:	r= write(device, bootblock, SECTOR_SIZE);	break;
1406 	}
1407 
1408 	return r;
1409 }
1410 
1411 int cylinderalign(region_t *reg)
1412 {
1413 	if(reg->is_used_part) {
1414 		if(reg->used_part.lowsec != table[0].lowsec + sectors
1415 			&& (reg->used_part.lowsec % secpcyl)) {
1416 			int extra;
1417 			extra = secpcyl - (reg->used_part.lowsec % secpcyl);
1418 			reg->used_part.lowsec += extra;
1419 			reg->used_part.size -= extra;
1420 		}
1421 		if((reg->used_part.size+1) % secpcyl) {
1422 			reg->used_part.size -= secpcyl - ((reg->used_part.size + 1) % secpcyl);
1423 		}
1424 		return reg->used_part.size > 0;
1425 	}
1426 
1427 	if(reg->free_sec_start != table[0].lowsec + sectors && (reg->free_sec_start % secpcyl)) {
1428 		/* Start is unaligned. Round up. */
1429 		reg->free_sec_start += secpcyl - (reg->free_sec_start % secpcyl);
1430 	}
1431 	if((reg->free_sec_last+1) % secpcyl) {
1432 		/* End is unaligned. Round down. */
1433 		reg->free_sec_last -= (reg->free_sec_last+1) % secpcyl;
1434 	}
1435 
1436 	/* Return nonzero if anything remains of the region after rounding. */
1437 	return reg->free_sec_last > reg->free_sec_start;
1438 }
1439 
1440 void regionize(void)
1441 {
1442 	int free_sec, i, si;
1443 
1444 	sort();
1445 
1446 	free_sec = table[0].lowsec + sectors;
1447 
1448 	/* Create region data used in autopart mode. */
1449 	free_regions = used_regions = nr_regions = nr_partitions = 0;
1450 	if(table[0].lowsec > table[sort_order[1]].lowsec &&
1451 		table[sort_order[1]].sysind != NO_PART) {
1452 		printf("\nSanity check failed on %s - first partition starts before disk.\n"
1453 			"Please use expert mode to correct it.\n", curdev->name);
1454 		exit(1);
1455 	}
1456 	for(si = 1; si <= NR_PARTITIONS; si++) {
1457 		i = sort_order[si];
1458 		if(i < 1 || i > NR_PARTITIONS) {
1459 			printf("Sorry, something unexpected has happened (%d out of range).\n", i);
1460 			exit(1);
1461 		}
1462 
1463 		if(table[i].sysind == NO_PART)
1464 			break;
1465 
1466 		/* Free space before this partition? */
1467 		if(table[i].lowsec > free_sec) {
1468 			/* Free region before this partition. */
1469 			regions[nr_regions].free_sec_start = free_sec;
1470 			regions[nr_regions].free_sec_last = table[i].lowsec-1;
1471 			regions[nr_regions].is_used_part = 0;
1472 			if(cylinderalign(&regions[nr_regions])) {
1473 				nr_regions++;
1474 				free_regions++;
1475 			}
1476 		}
1477 
1478 		/* Sanity check. */
1479 		if(si > 1) {
1480 			if(table[i].lowsec < table[sort_order[si-1]].lowsec ||
1481 			   table[i].lowsec < table[sort_order[si-1]].lowsec + table[sort_order[si-1]].size) {
1482 				printf("\nSanity check failed on %s - partitions overlap.\n"
1483 					"Please use expert mode to correct it.\n", curdev->name);
1484 				exit(1);
1485 			}
1486 		}
1487 		if(table[i].size > table[0].size) {
1488 			printf("\nSanity check failed on %s - partition is larger than disk.\n"
1489 				"Please use expert mode to correct it.\n", curdev->name);
1490 			exit(1);
1491 		}
1492 		if(table[i].size < 1) {
1493 			printf("\nSanity check failed on %s - zero-sized partition.\n"
1494 				"Please use expert mode to correct it.\n", curdev->name);
1495 			exit(1);
1496 		}
1497 
1498 		/* Remember used region. */
1499 		memcpy(&regions[nr_regions].used_part, &table[i], sizeof(table[i]));
1500 		free_sec = table[i].lowsec+table[i].size;
1501 		regions[nr_regions].is_used_part = 1;
1502 		regions[nr_regions].tableno = i;
1503 		nr_partitions++;
1504 		nr_regions++;
1505 		used_regions++;
1506 	}
1507 
1508 	/* Special case: space after partitions. */
1509 	if(free_sec <   table[0].lowsec + table[0].size-1) {
1510 		regions[nr_regions].free_sec_start = free_sec;
1511 		regions[nr_regions].free_sec_last = table[0].lowsec + table[0].size-1;
1512 		regions[nr_regions].is_used_part = 0;
1513 		if(cylinderalign(&regions[nr_regions])) {
1514 			nr_regions++;
1515 			free_regions++;
1516 		}
1517 	}
1518 
1519 }
1520 
1521 void m_read(int ev, int *biosdrive)
1522 /* Read the partition table from the current device. */
1523 {
1524 	int i, mode, n, v;
1525 	struct part_entry *pe;
1526 	u32_t system_hz;
1527 
1528 	if (ev != 'r' || device >= 0) return;
1529 
1530 	/* Open() may cause kernel messages: */
1531 	stat_start(0);
1532 	fflush(stdout);
1533 
1534 	if ((device= open(curdev->name, mode= O_RDWR, 0666)) < 0) {
1535 		if (device >= 0) { close(device); device= -1; }
1536 		return;
1537 	}
1538 
1539 	system_hz = (u32_t) sysconf(_SC_CLK_TCK);
1540 	v = 2*system_hz;
1541 	ioctl(device, DIOCTIMEOUT, &v);
1542 
1543 	memset(bootblock, 0, sizeof(bootblock));
1544 
1545 	n= boot_readwrite(0);
1546 
1547 	if (n <= 0) stat_start(1);
1548 	if (n < 0) {
1549 		close(device);
1550 		device= -1;
1551 	} else
1552 	if (n < SECTOR_SIZE) {
1553 		close(device);
1554 		device= -1;
1555 		return;
1556 	}
1557 	if (n <= 0) stat_end(5);
1558 
1559 	if (n < SECTOR_SIZE) n= SECTOR_SIZE;
1560 
1561 	if(biosdrive) (*biosdrive)++;
1562 
1563 	if(!open_ct_ok(device)) {
1564 		printf("\n%s: device in use! skipping it.", curdev->subname);
1565 		fflush(stdout);
1566 		close(device);
1567 		device= -1;
1568 		return;
1569 	}
1570 
1571 	memcpy(table+1, bootblock+PART_TABLE_OFF,
1572 					NR_PARTITIONS * sizeof(table[1]));
1573 	if (bootblock[510] != 0x55 || bootblock[511] != 0xAA) {
1574 		/* Invalid boot block, install bootstrap, wipe partition table.
1575 		 */
1576 		memset(bootblock, 0, sizeof(bootblock));
1577 		installboot(bootblock, MASTERBOOT);
1578 		memset(table+1, 0, NR_PARTITIONS * sizeof(table[1]));
1579 	}
1580 
1581 	/* Fix an extended partition table up to something mere mortals can
1582 	 * understand.  Record already defined partitions.
1583 	 */
1584 	for (i= 1; i <= NR_PARTITIONS; i++) {
1585 		pe= &table[i];
1586 		if (extbase != 0 && pe->sysind != NO_PART)
1587 			pe->lowsec+= ext_part(pe->sysind) ? extbase : offset;
1588 		existing[i]= pe->sysind != NO_PART;
1589 	}
1590 	geometry();
1591 	dirty= 0;
1592 
1593 	/* Warn about grave dangers ahead. */
1594 	if (extbase != 0) {
1595 		stat_start(1);
1596 		printf("Warning: You are in an extended partition.");
1597 		stat_end(5);
1598 	}
1599 
1600 	regionize();
1601 }
1602 
1603 void m_write(int ev, object_t *op)
1604 /* Write the partition table back if modified. */
1605 {
1606 	struct part_entry new_table[NR_PARTITIONS], *pe;
1607 
1608 	if (ev != 'w' && ev != E_WRITE) return;
1609 	if (device < 0) { dirty= 0; return; }
1610 	if (!dirty) {
1611 		if (ev == 'w') {
1612 			stat_start(1);
1613 			printf("%s is not changed, or has already been written",
1614 							curdev->subname);
1615 			stat_end(2);
1616 		}
1617 		return;
1618 	}
1619 
1620 	if (extbase != 0) {
1621 		/* Will this stop him?  Probably not... */
1622 		stat_start(1);
1623 		printf("You have changed an extended partition.  Bad Idea.");
1624 		stat_end(5);
1625 	}
1626 
1627 	memcpy(new_table, table+1, NR_PARTITIONS * sizeof(table[1]));
1628 	for (pe= new_table; pe < new_table + NR_PARTITIONS; pe++) {
1629 		if (pe->sysind == NO_PART) {
1630 			memset(pe, 0, sizeof(*pe));
1631 		} else {
1632 			abs2dos(&pe->start_head, pe->lowsec);
1633 			abs2dos(&pe->last_head, pe->lowsec + pe->size - 1);
1634 
1635 			/* Fear and loathing time: */
1636 			if (extbase != 0)
1637 				pe->lowsec-= ext_part(pe->sysind)
1638 						? extbase : offset;
1639 		}
1640 	}
1641 	memcpy(bootblock+PART_TABLE_OFF, new_table, sizeof(new_table));
1642 	bootblock[510]= 0x55;
1643 	bootblock[511]= 0xAA;
1644 
1645 	if (boot_readwrite(1) < 0) {
1646 		stat_start(1);
1647 		printf("%s: %s", curdev->name, strerror(errno));
1648 		stat_end(5);
1649 		return;
1650 	}
1651 	dirty= 0;
1652 }
1653 
1654 void m_shell(int ev, object_t *op)
1655 /* Shell escape, to do calculations for instance. */
1656 {
1657 	int r, pid, status;
1658 	void (*sigint)(int), (*sigquit)(int), (*sigterm)(int);
1659 
1660 	if (ev != 's') return;
1661 
1662 	reset_tty();
1663 	fflush(stdout);
1664 
1665 	switch (pid= fork()) {
1666 	case -1:
1667 		stat_start(1);
1668 		printf("can't fork: %s\n", strerror(errno));
1669 		stat_end(3);
1670 		break;
1671 	case 0:
1672 		if (device >= 0) (void) close(device);
1673 		execl("/bin/sh", "sh", (char *) nil);
1674 		r= errno;
1675 		stat_start(1);
1676 		printf("/bin/sh: %s\n", strerror(errno));
1677 		stat_end(3);
1678 		exit(127);
1679 	}
1680 	sigint= signal(SIGINT, SIG_IGN);
1681 	sigquit= signal(SIGQUIT, SIG_IGN);
1682 	sigterm= signal(SIGTERM, SIG_IGN);
1683 	while (pid >= 0 && (r= wait(&status)) >= 0 && r != pid) {}
1684 	(void) signal(SIGINT, sigint);
1685 	(void) signal(SIGQUIT, sigquit);
1686 	(void) signal(SIGTERM, sigterm);
1687 	tty_raw();
1688 	if (pid < 0)
1689 		;
1690 	else
1691 	if (WIFEXITED(status) && WEXITSTATUS(status) == 127)
1692 		stat_start(0);	/* Match the stat_start in the child. */
1693 	else
1694 		event(ctrl('L'), op);
1695 }
1696 
1697 int quitting= 0;
1698 
1699 void m_quit(int ev, object_t *op)
1700 /* Write the partition table if modified and exit. */
1701 {
1702 	if (ev != 'q' && ev != 'x') return;
1703 
1704 	quitting= 1;
1705 
1706 	if (dirty) event(E_WRITE, op);
1707 	if (dirty) quitting= 0;
1708 }
1709 
1710 void m_help(int ev, object_t *op)
1711 /* For people without a clue; let's hope they can find the '?' key. */
1712 {
1713 	static struct help {
1714 		char	*keys;
1715 		char	*what;
1716 	} help[]= {
1717 	 { "? !",		 "This help / more advice!" },
1718 	 { "+ - (= _ PgUp PgDn)","Select/increment/decrement/make active" },
1719 	 { "0-9 (a-f)",		 "Enter value" },
1720 	 { "hjkl (arrow keys)",	 "Move around" },
1721 	 { "CTRL-K CTRL-J",	 "Move entry up/down" },
1722 	 { "CTRL-L",		 "Redraw screen" },
1723 	 { ">",			 "Start a subpartition table" },
1724 	 { "<",			 "Back to the primary partition table" },
1725 	 { "m",			 "Cycle through magic values" },
1726 	 { "spacebar",		 "Show \"Size\" or \"Last\"" },
1727 	 { "r w",		 "Read/write partition table" },
1728 	 { "p s q x",		 "Raw dump / Shell escape / Quit / Exit" },
1729 	 { "y n DEL",		 "Answer \"yes\", \"no\", \"cancel\"" },
1730 	};
1731 	static char *advice[] = {
1732 "* Choose a disk with '+' and '-', then hit 'r'.",
1733 "* To change any value: Move to it and use '+', '-' or type the desired value.",
1734 "* To make a new partition:  Move over to the Size or Kb field of an unused",
1735 "  partition and type the size.  Hit the 'm' key to pad the partition out to",
1736 "  a cylinder boundary.  Hit 'm' again to pad it out to the end of the disk.",
1737 "  You can hit 'm' more than once on a base or size field to see several",
1738 "  interesting values go by.  Note: Other Operating Systems can be picky about",
1739 "  partitions that are not padded to cylinder boundaries.  Look for highlighted",
1740 "  head or sector numbers.",
1741 "* To reuse a partition:  Change the type to MINIX.",
1742 "* To delete a partition:  Type a zero in the hex Type field.",
1743 "* To make a partition active:  Type '+' in the Num field.",
1744 "* To study the list of keys:  Type '?'.",
1745 	};
1746 
1747 	if (ev == '?') {
1748 		struct help *hp;
1749 
1750 		for (hp= help; hp < arraylimit(help); hp++) {
1751 			stat_start(0);
1752 			printf("%-25s - %s", hp->keys, hp->what);
1753 			stat_end(0);
1754 		}
1755 		stat_start(0);
1756 		putstr("Things like ");
1757 		putstr(t_so); putstr("this"); putstr(t_se);
1758 		putstr(" must be checked, but ");
1759 		putstr(t_md); putstr("this"); putstr(t_me);
1760 		putstr(" is not really a problem");
1761 		stat_end(0);
1762 	} else
1763 	if (ev == '!') {
1764 		char **ap;
1765 
1766 		for (ap= advice; ap < arraylimit(advice); ap++) {
1767 			stat_start(0);
1768 			putstr(*ap);
1769 			stat_end(0);
1770 		}
1771 	}
1772 }
1773 
1774 void event(int ev, object_t *op)
1775 /* Simply call all modifiers for an event, each one knows when to act. */
1776 {
1777 	m_help(ev, op);
1778 	m_redraw(ev, op);
1779 	m_toggle(ev, op);
1780 	m_orientation(ev, op);
1781 	m_move(ev, op);
1782 	m_updown(ev, op);
1783 	m_enter(ev, op);
1784 	m_leave(ev, op);
1785 	m_modify(ev, op);
1786 	m_magic(ev, op);
1787 	m_in(ev, op);
1788 	m_out(ev, op);
1789 	m_read(ev, NULL);
1790 	m_write(ev, op);
1791 	m_shell(ev, op);
1792 	m_quit(ev, op);
1793 }
1794 
1795 char *
1796 prettysizeprint(int kb)
1797 {
1798 	int toosmall = 0;
1799 	static char str[200];
1800 	char unit = 'k';
1801 	if(MIN_REGION_SECTORS > kb*2)
1802 		toosmall = 1;
1803 	if(kb >= 5*1024) {
1804 		kb /= 1024;
1805 		unit = 'M';
1806 		if(kb >= 5*1024) {
1807 			kb /= 1024;
1808 			unit = 'G';
1809 		}
1810 	}
1811 	sprintf(str, "%4d %cB%s", kb, unit,
1812 		toosmall ? ", too small for MINIX 3" : "");
1813 	return str;
1814 }
1815 
1816 void
1817 printregions(region_t *theregions, int indent, int p_nr_partitions, int p_free_regions, int p_nr_regions, int numbers)
1818 {
1819 	int r, nofree = 0;
1820 	region_t *reg;
1821 	reg = theregions;
1822 
1823 	if((p_nr_partitions >= NR_PARTITIONS || !p_free_regions) && p_free_regions)
1824 		nofree = 1;
1825 	for(r = 0; r < p_nr_regions; r++, reg++) {
1826 		unsigned long units;
1827 		if(reg->is_used_part) {
1828 			char *name;
1829 			name = typ2txt(reg->used_part.sysind);
1830 			printf("%*s", indent, ""); type2col(reg->used_part.sysind);
1831 			if(numbers) printf("[%d]  ", r);
1832 			printf("In use by %-10s ", name);
1833 			units = reg->used_part.size / 2;
1834 			col(0);
1835 			printf(" (%s)\n", prettysizeprint(units));
1836 		} else {
1837 			printf("%*s", indent, "");
1838 			if(numbers) {
1839 				if(!nofree) printf("[%d]  ", r);
1840 				else printf("[-]  ");
1841 			}
1842 			printf("Free space           ");
1843 			units = ((reg->free_sec_last - reg->free_sec_start+1))/2;
1844 			printf(" (%s)\n", prettysizeprint(units));
1845 		}
1846 	}
1847 
1848 	if(numbers && p_nr_partitions >= NR_PARTITIONS && p_free_regions) {
1849 		printf(
1850 "\nNote: there is free space on this disk, but you can't select it,\n"
1851 "because there isn't a free slot in the partition table to use it.\n"
1852 "You can reclaim the free space by deleting an adjacent region.\n");
1853 	}
1854 
1855 	return;
1856 }
1857 
1858 #define IS_YES   3
1859 #define IS_NO    4
1860 #define IS_OTHER 5
1861 int
1862 is_sure(char *fmt, ...)
1863 {
1864 	char yesno[10];
1865 	va_list ap;
1866 	va_start (ap, fmt);
1867 	vprintf(fmt, ap);
1868 	va_end(ap);
1869 	printf("  Please enter 'yes' or 'no': ");
1870 	fflush(stdout);
1871 	if(!fgets(yesno, sizeof(yesno)-1, stdin)) exit(1);
1872 
1873 	if (strcmp(yesno, "yes\n") == 0) return(IS_YES);
1874 	if (strcmp(yesno, "no\n") == 0) return(IS_NO);
1875 	return IS_OTHER;
1876 }
1877 
1878 void warn(char *message)
1879 {
1880 	printf("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b ! %s\n",message);
1881 }
1882 
1883 int
1884 may_kill_region(void)
1885 {
1886         int confirmation;
1887 	char line[100];
1888 	int r, i;
1889 
1890 	if(used_regions < 1) return 1;
1891 
1892 	printf("\n -- Delete in-use region? --\n\n");
1893 
1894 	printregions(regions, 3, nr_partitions, free_regions, nr_regions, 1);
1895 	printf("\nEnter the region number to delete or ENTER to continue: ");
1896 	fflush(NULL);
1897 	fgets(line, sizeof(line)-2, stdin);
1898 	if(!isdigit(line[0]))
1899 		return 1;
1900 
1901 	r=atoi(line);
1902 	if(r < 0 || r >= nr_regions) {
1903 		printf("This choice is out of range.\n");
1904 		return 0;
1905 	}
1906 
1907 	if(!regions[r].is_used_part) {
1908 		printf("This region is not in use.\n");
1909 		return 0;
1910 	}
1911 
1912 	i = regions[r].tableno;
1913 
1914 	printf("\nPlease confirm that you want to delete region %d, losing all data it", r);
1915 	printf("\ncontains. You're disk is not actually updated right away, but still.");
1916 	printf("\n\n");
1917 
1918  	do {
1919 		confirmation = is_sure("Are you sure you want to continue?");
1920 		if (confirmation == IS_NO) return 0;
1921 	} while (confirmation != IS_YES);
1922 
1923 		table[i].sysind = NO_PART;
1924 		dirty = 1;
1925 		regionize();
1926 
1927 	/* User may go again. */
1928 	return 0;
1929 }
1930 
1931 
1932 region_t *
1933 select_region(void)
1934 {
1935 	int rn, done = 0;
1936 	static char line[100];
1937 	int nofree = 0;
1938 
1939 	printstep(2, "Select a disk region");
1940 
1941 	if(nr_regions < 1) {
1942 		printf("\nNo regions found - maybe the drive is too small.\n"
1943 			"Please try expert mode.\n");
1944 		exit(1);
1945 	}
1946 
1947 	if(nr_partitions >= NR_PARTITIONS || !free_regions) {
1948 		if(free_regions) {
1949 			nofree = 1;
1950 		}
1951 	}
1952 
1953 
1954 	printf("\nPlease select the region that you want to use for the MINIX 3 setup.");
1955 	printf("\nIf you select an in-use region it will be overwritten by MINIX. The");
1956 	printf("\nfollowing region%s were found on the selected disk:\n\n",
1957 		SORNOT(nr_regions));
1958 	printregions(regions, 3, nr_partitions, free_regions, nr_regions, 1);
1959 
1960 
1961 	printf("\n");
1962 	do {
1963 		printf("Enter the region number to use or type 'delete': ");
1964 		if(nr_regions == 1) printf(" [0] ");
1965 		fflush(NULL);
1966 
1967 		if(!fgets(line, sizeof(line)-2, stdin))
1968 			exit(1);
1969 
1970 		if (nr_regions == 1 && line[0] == '\n') {
1971 		    rn = 0;
1972 		    done = 1;
1973 		}
1974 		else {
1975 			if(strcmp(line,"delete\n") == 0) {
1976 				may_kill_region();
1977 				return NULL;
1978 			}
1979 
1980 			if(sscanf(line, "%d", &rn) != 1)  {
1981 				warn("invalid choice");
1982 				continue;
1983 			}
1984 
1985 			if(rn < 0 || rn >= nr_regions) {
1986 				warn("out of range");
1987 				continue;
1988 			}
1989 
1990 			if(nofree && !regions[rn].is_used_part) {
1991 				warn("not available");
1992 				continue;
1993 			}
1994 
1995 			done = 1;
1996 		}
1997 	} while(! done);
1998 
1999 	return(&regions[rn]);
2000 }
2001 
2002 void printstep(int step, char *str)
2003 {
2004 	int n;
2005 	n = printf("\n --- Substep 3.%d: %s ---", step, str);
2006 	while(n++ < 73) printf("-");
2007 	printf("\n");
2008 }
2009 
2010 device_t *
2011 select_disk(void)
2012 {
2013 	int done = 0;
2014 	int i, choice, drives;
2015 	static char line[500];
2016 	int biosdrive = 0;
2017 
2018 	printstep(1, "Select a disk to install MINIX 3");
2019 	printf("\nProbing for disks. This may take a short while.");
2020 
2021 		i = 0;
2022 		curdev=firstdev;
2023 
2024 		for(; i < MAX_DEVICES;) {
2025 			printf(".");
2026 			fflush(stdout);
2027 			m_read('r', &biosdrive);
2028 			if(device >= 0) {
2029 				devices[i].dev = curdev;
2030 				devices[i].free_regions = free_regions;
2031 				devices[i].nr_regions = nr_regions;
2032 				devices[i].nr_partitions = nr_partitions;
2033 				devices[i].used_regions = used_regions;
2034 				devices[i].sectors = table[0].size;
2035 				curdev->biosdrive = biosdrive-1;
2036 				memcpy(devices[i].regions, regions, sizeof(regions));
2037 				i++;
2038 			}
2039 
2040 			nextdevice(NULL, 1);
2041 			if(curdev == firstdev)
2042 				break;
2043 		}
2044 
2045 		drives = i;
2046 
2047 		if(drives < 1) {
2048 			printf("\nFound no drives - can't partition.\n");
2049 			exit(1);
2050 		}
2051 
2052 		printf(" Probing done.\n");
2053 		printf("The following disk%s %s found on your system:\n\n", SORNOT(drives),
2054 			drives == 1 ? "was" : "were");
2055 
2056 			for(i = 0; i < drives; i++) {
2057 				printf("  ");
2058 				printf("Disk [%d]:  ", i);
2059 				printf("%s, ", devices[i].dev->name);
2060 				printf("%s\n", prettysizeprint(devices[i].sectors/2));
2061 				printregions(devices[i].regions, 8,
2062 					devices[i].nr_partitions,
2063 					devices[i].free_regions,
2064 					devices[i].nr_regions, 0);
2065 			}
2066 
2067 	   printf("\n");
2068 		do {
2069 			printf("Enter the disk number to use: ");
2070 	   		if (drives == 1) printf("[0] ");
2071 			fflush(NULL);
2072 			if(!fgets(line, sizeof(line)-2, stdin))
2073 				exit(1);
2074 			if (line[0] == '\n' && drives == 1) {
2075 				choice = 0;
2076 				done = 1;
2077 			} else {
2078 			    if(sscanf(line, "%d", &choice) != 1) {
2079 				warn("choose a disk");
2080 			 	continue;
2081 			    }
2082 			    if(choice < 0 || choice >= i) {
2083 				warn("out of range");
2084 				continue;
2085 			    }
2086 			    done = 1;
2087 			}
2088 		} while(! done);
2089 	return devices[choice].dev;
2090 }
2091 
2092 int
2093 scribble_region(region_t *reg, struct part_entry **pe, int *made_new)
2094 {
2095 	int ex, changed = 0, i;
2096 	struct part_entry *newpart;
2097 	if(!reg->is_used_part) {
2098 		ex = reg->free_sec_last - reg->free_sec_start + 1;
2099 		if(made_new) *made_new = 1;
2100 	} else if(made_new) *made_new = 0;
2101 	if(!reg->is_used_part) {
2102 		for(i = 1; i <= NR_PARTITIONS; i++)
2103 			if(table[i].sysind == NO_PART)
2104 				break;
2105 		if(i > NR_PARTITIONS) {
2106 			/* Bug, should've been caught earlier. */
2107 			printf("Couldn't find a free slot. Please try expert mode.\n");
2108 			exit(1);
2109 		}
2110 		newpart = &table[i];
2111 		newpart->lowsec = reg->free_sec_start;
2112 		newpart->size = reg->free_sec_last - reg->free_sec_start + 1;
2113 		changed = 1;
2114 		newpart->sysind = MINIX_PART;
2115 	} else  {
2116 		newpart = &reg->used_part;
2117 	}
2118 	*pe = newpart;
2119 	changed = 1;
2120 	dirty = 1;
2121 	return changed;
2122 }
2123 
2124 int
2125 sanitycheck_failed(char *dev, struct part_entry *pe)
2126 {
2127 	struct part_geom part;
2128 	int fd;
2129 	unsigned long it_lowsec, it_secsize;
2130 
2131 	if((fd = open(dev, O_RDONLY)) < 0) {
2132 		perror(dev);
2133 		return 1;
2134 	}
2135 
2136 	if (ioctl(fd, DIOCGETP, &part) < 0) {
2137 		fprintf(stderr, "DIOCGETP failed\n");
2138 		perror(dev);
2139 		return 1;
2140 	}
2141 
2142 	if(!open_ct_ok(fd)) {
2143 		printf("\nAutopart error: the disk is in use. This means that although a\n"
2144 			"new table has been written, it won't be in use by the system\n"
2145 			"until it's no longer in use (or a reboot is done). Just in case,\n"
2146 			"I'm not going to continue. Please un-use the disk (or reboot) and try\n"
2147 			"again.\n\n");
2148 		return 1;
2149 	}
2150 
2151 	close(fd);
2152 
2153 	it_lowsec  = (unsigned long)(part.base / SECTOR_SIZE);
2154 	it_secsize = (unsigned long)(part.size / SECTOR_SIZE);
2155 
2156 	if(it_lowsec != pe->lowsec || it_secsize != pe->size) {
2157 		fprintf(stderr, "\nReturned and set numbers don't match up!\n");
2158 		fprintf(stderr, "This can happen if the disk is still opened.\n");
2159 		return 1;
2160 	}
2161 
2162 	return 0;
2163 }
2164 
2165 int
2166 do_autopart(int resultfd)
2167 {
2168 	int confirmation;
2169 	region_t *r;
2170 	struct part_entry *pe;
2171 	struct part_entry orig_table[1 + NR_PARTITIONS];
2172 	int region, newp;
2173 
2174 	nordonly = 1;
2175 
2176 	do {
2177 		curdev = select_disk();
2178 	} while(!curdev);
2179 
2180 	if(device >= 0) {
2181 		close(device);
2182 		device = -1;
2183 	}
2184 	recompute0();
2185 
2186 	m_read('r', NULL);
2187 
2188 	memcpy(orig_table, table, sizeof(table));
2189 
2190 	do {
2191 		/* Show regions. */
2192 		r = select_region();
2193 	} while(!r);	/* Back to step 2. */
2194 
2195 	/* Write things. */
2196 	if(scribble_region(r, &pe, &newp)) {
2197 		char *name;
2198 		int i, found = -1;
2199 		char partbuf[100], devname[100];
2200 		struct part_entry *tpe = NULL;
2201 
2202 		printstep(3, "Confirm your choices");
2203 
2204 		region =  (int)(r-regions);
2205 		/* disk = (int) (curdev-devices); */
2206 
2207 		printf("\nThis is the point of no return.  You have selected to install MINIX 3\n");
2208 		printf("into region %d of disk %s.  Please confirm that you want\n",
2209 			region, curdev->name);
2210 		printf("to use this selection to install MINIX 3.\n\n");
2211 
2212 		do {
2213 			confirmation = is_sure("Are you sure you want to continue?");
2214 			if (confirmation == IS_NO) return 1;
2215 		} while (confirmation != IS_YES);
2216 
2217 		/* Retrieve partition number in sorted order that we
2218 		 * have scribbled in.
2219 		 */
2220 		sort();
2221 		for(i = 1; i <= NR_PARTITIONS; i++) {
2222 			int si;
2223 			si = sort_order[i];
2224 			if(si < 1 || si > NR_PARTITIONS) {
2225 				fprintf(stderr, "Autopart internal error (out of range) (nothing written).\n");
2226 				exit(1);
2227 			}
2228 			if(table[si].lowsec == pe->lowsec) {
2229 				if(found > 0) {
2230 					fprintf(stderr, "Autopart internal error (part found twice) (nothing written).\n");
2231 					exit(1);
2232 				}
2233 				check_ind(&table[si]);
2234 				table[si].sysind = MINIX_PART;
2235 				found = i;
2236 				tpe = &table[si];
2237 			}
2238 		}
2239 		if(found < 1) {
2240 			fprintf(stderr, "Autopart internal error (part not found) (nothing written).\n");
2241 			exit(1);
2242 		}
2243 		m_write('w', NULL);
2244 		if(dirty) {
2245 			fprintf(stderr, "Autopart internal error (couldn't update disk).\n");
2246 			exit(1);
2247 		}
2248 		name=strrchr(curdev->name, '/');
2249 		if(!name) name = curdev->name;
2250 		else name++;
2251 
2252 		sprintf(partbuf, "%sp%d d%dp%d\n", name, found-1,
2253 			curdev->biosdrive, found-1);
2254 		sprintf(devname, "/dev/%sp%d", name, found-1);
2255 		if(resultfd >= 0 && write(resultfd, partbuf, strlen(partbuf)) < strlen(partbuf)) {
2256 			fprintf(stderr, "Autopart internal error (couldn't write result).\n");
2257 			exit(1);
2258 		}
2259 		if(device >= 0) {
2260 			close(device);
2261 			device = -1;
2262 		}
2263 
2264 #if 0
2265 		m_dump(orig_table);
2266 		printf("\n");
2267 		m_dump(table);
2268 #endif
2269 
2270 		if(sanitycheck_failed(devname, tpe)) {
2271 			fprintf(stderr, "Autopart internal error (disk sanity check failed).\n");
2272 			exit(1);
2273 		}
2274 
2275 		if(newp) {
2276 			int fd;
2277 			if((fd=open(devname, O_WRONLY)) < 0) {
2278 				perror(devname);
2279 			} else {
2280 				/* Clear any subpartitioning. */
2281 				static unsigned char sub[2048];
2282 				sub[510] = 0x55;
2283 				sub[511] = 0xAA;
2284 				write(fd, sub, sizeof(sub));
2285 				close(fd);
2286 			}
2287 		}
2288 		return 0;
2289 	}
2290 
2291 	return 1;
2292 }
2293 
2294 int main(int argc, char **argv)
2295 {
2296      	int c;
2297 	int i, key;
2298 	int resultfd = -1;
2299 
2300      	/* autopart uses getopt() */
2301      	while((c = getopt(argc, argv, "m:f:")) != EOF) {
2302      		switch(c) {
2303 			case 'm':
2304 				min_region_mb = atoi(optarg);
2305 				break;
2306      			case 'f':
2307 				/* Make sure old data file is gone. */
2308      				unlink(optarg);
2309      				if((resultfd=open(optarg, O_CREAT | O_WRONLY | O_TRUNC)) < 0) {
2310      					perror(optarg);
2311      					return 1;
2312      				}
2313      				sync();	/* Make sure no old data file lingers. */
2314      				break;
2315      			default:
2316      				fprintf(stderr, "Unknown option\n");
2317      				return 1;
2318      		}
2319      	}
2320 
2321      argc -= optind;
2322      argv += optind;
2323 
2324 	for (i= 0; i < argc; i++) {
2325 	 newdevice(argv[i], 0, 0);
2326 	 }
2327 
2328 	if (firstdev == nil) {
2329 		getdevices();
2330 		key= ctrl('L');
2331 	} else {
2332 		key= 'r';
2333 	}
2334 
2335         {
2336 		int r;
2337 		if (firstdev == nil) {
2338 			fprintf(stderr, "autopart couldn't find any devices.\n");
2339 			return 1;
2340 		}
2341 		r = do_autopart(resultfd);
2342 		if(resultfd >= 0) { close(resultfd); }
2343 		return r;
2344 	}
2345 
2346 	exit(0);
2347 }
2348