sacc

sacc - sacc(omys), simple console gopher client (config)
git clone git://git.codemadness.org/sacc
Log | Files | Refs | LICENSE

ui_rogue.c (29707B)


      1 #include <errno.h>
      2 #include <signal.h>
      3 #include <stdarg.h>
      4 #include <stdint.h>
      5 #include <stdio.h>
      6 #include <stdlib.h>
      7 #include <string.h>
      8 #include <term.h>
      9 #include <termios.h>
     10 #include <unistd.h>
     11 #include <sys/select.h>
     12 #include <sys/types.h>
     13 
     14 #include "common.h"
     15 #include "config.h"
     16 
     17 #define C(c) #c
     18 #define S(c) C(c)
     19 
     20 /* ncurses doesn't define those in term.h, where they're used */
     21 #ifndef OK
     22 #define OK (0)
     23 #endif
     24 #ifndef ERR
     25 #define ERR (-1)
     26 #endif
     27 
     28 #define maplines (lines - 2)
     29 
     30 enum {
     31 	Blocks = 1,
     32 	Standout = 2,
     33 	Important = 4
     34 };
     35 
     36 struct cell;
     37 struct tile {
     38 	char c;
     39 	char flags;
     40 	char *name;
     41 	char *description;
     42 	char *afterinteract;
     43 	Item *(*interact)(struct cell *);
     44 };
     45 
     46 struct cell {
     47 	struct tile *tile;
     48 	size_t nitems;
     49 	Item **items;
     50 };
     51 
     52 struct room {
     53 	size_t x, y;
     54 	size_t w, h;
     55 };
     56 
     57 struct rect {
     58 	struct rect *next, *next2;
     59 	struct rect *p;
     60 	size_t x1, y1;
     61 	size_t x2, y2;
     62 	size_t d;
     63 	union {
     64 		void *p;
     65 		int i;
     66 	} data;
     67 };
     68 
     69 static struct termios tsave;
     70 static struct termios tsacc;
     71 static Item *curentry;
     72 static int termset = ERR;
     73 static char bufout[256];
     74 static char bufout2[256];
     75 
     76 size_t ox, oy;
     77 size_t px, py;
     78 
     79 #define MAPHEIGHT (50)
     80 #define MAPWIDTH (160)
     81 struct cell map[MAPHEIGHT][MAPWIDTH];
     82 
     83 enum {
     84 	DungeonScreen,
     85 	MenuScreen
     86 } screen;
     87 
     88 Item *interactitem(struct cell *);
     89 Item *interactmenu(struct cell *);
     90 
     91 struct tile tile_void = { ' ', Blocks, "Void", "The void. The thing which is everywhere where nothing is.", NULL, NULL };
     92 struct tile tile_floor = { '.', 0, "Floor", "An ordinary stone floor.", NULL, NULL };
     93 struct tile tile_corridor = { '#', 0, "Different Floor", "This floor looks different than the other one.", NULL, NULL };
     94 struct tile tile_verticalwall = { '|', Blocks, "Wall", "Wall.", NULL, NULL };
     95 struct tile tile_horizontalwall = { '-', Blocks, "Wall", "Wall.", NULL, NULL };
     96 struct tile tile_door = { '/', 0, "Door", "A door.", NULL, NULL };
     97 struct tile tile_bookshelf = { 'E', Important, "Bookshelf", "A bookshelf.", "A loading bar?! In a book?!", interactmenu };
     98 struct tile tile_book = { '?', Important, "%s", "A book: '%s'.", "A loading bar?! In a book?!", interactitem };
     99 struct tile tile_portal = { '0', Important, "%s", "A portal: '%s'.", "You are getting transported through time and space.", interactitem };
    100 struct tile tile_portalmachine = { 'O', Important, "Portal Machine", "A portal machine.", "You are getting transported through time and space.", interactmenu };
    101 struct tile tile_heapofstuff = { '%', Important, "Heap", "A heap of stuff.", "The thing you touches glows strangely...", interactmenu };
    102 struct tile tile_elevator = { 'L', Important, "Elevator", "An elevator.", "You hear elevator music...", interactmenu };
    103 struct tile tile_stairsdown = { '>', Important, "'%s'", "A staircase leading down: '%s'.", "Too many stairs...", interactitem };
    104 
    105 struct tile tile_stairsup = { '<', Standout | Important, "'%s'", "A staircase leading up: '%s'.", "Too many stairs...", interactitem };
    106 struct tile tile_backportal = { '0', Standout | Important, "'%s'", "A portal leading back to wherever you came from: '%s'.", "You are getting transported through time and space.", interactitem };
    107 
    108 void drawscreen(void);
    109 
    110 int
    111 mygetchar_(void)
    112 {
    113 	int r;
    114 	fd_set fdset;
    115 
    116 	FD_ZERO(&fdset);
    117 	FD_SET(0, &fdset);
    118 
    119 	if ((r = select(1, &fdset, NULL, NULL, NULL)) == -1) {
    120 		if (errno == EINTR)
    121 			return -1;
    122 		return -2;
    123 	}
    124 
    125 	return getchar();
    126 }
    127 
    128 volatile sig_atomic_t sigwinch;
    129 
    130 int
    131 mygetchar(void)
    132 {
    133 	int r;
    134 
    135 	while ((r = mygetchar_()) == -1) {
    136 		if (sigwinch) {
    137 			sigwinch = 0;
    138 
    139 			if (termset == OK)
    140 				del_curterm(cur_term);
    141 			termset = setupterm(NULL, 1, NULL);
    142 
    143 			drawscreen();
    144 		}
    145 	}
    146 
    147 	if (r == -2)
    148 		die("mygetchar: %s", strerror(errno));
    149 
    150 	return r;
    151 }
    152 
    153 /*
    154 	FNV-1a ( http://www.isthe.com/chongo/tech/comp/fnv/ )
    155 	FNV was published into the public domain ( https://creativecommons.org/publicdomain/zero/1.0/ )
    156 	by Landon Curt Noll: http://www.isthe.com/chongo/tech/comp/fnv/#public_domain
    157 */
    158 uint32_t
    159 fnv1a(int n,...)
    160 {
    161 	int i;
    162 	char *s;
    163 	va_list l;
    164 	uint32_t h;
    165 
    166 	h = 0x811c9dc5;
    167 
    168 	va_start(l, n);
    169 	for (i = 0; i < n; i++) {
    170 		for (s = va_arg(l, char*); *s; s++) {
    171 			h ^= *s;
    172 			h *= 0x01000193;
    173 		}
    174 	}
    175 	va_end(l);
    176 
    177 	return h;
    178 }
    179 
    180 /*
    181 	An LCG using the constants from "Numerical Recipes".
    182 */
    183 uint16_t
    184 ranqd1(uint32_t *s)
    185 {
    186 	return (*s = 1664525 * (*s) + 1013904223) >> 16;
    187 }
    188 
    189 struct rect *
    190 randomneighbor(struct rect *x, struct rect *rs, uint32_t *prng, int (*filter)(struct rect *, struct rect *))
    191 {
    192 	struct rect *r, *result;
    193 	size_t n;
    194 
    195 	n = 0;
    196 	result = NULL;
    197 	for (r = rs; r; r = r->next) {
    198 		if (r == x)
    199 			continue;
    200 		if (r->y2 < x->y1 || r->y1 > x->y2 || r->x2 < x->x1 || r->x1 > x->x2)
    201 			continue;
    202 		if ((r->y2 == x->y1 || r->y1 == x->y2) && (r->x2 == x->x1 || r->x1 == x->x2))
    203 			continue;
    204 		if (!filter(x, r))
    205 			continue;
    206 		n++;
    207 		if (ranqd1(prng) / (1. + UINT16_MAX) < 1. / n)
    208 			result = r;
    209 	}
    210 
    211 	return result;
    212 }
    213 
    214 size_t
    215 min(size_t a, size_t b)
    216 {
    217 	if (a < b)
    218 		return a;
    219 	return b;
    220 }
    221 
    222 size_t
    223 max(size_t a, size_t b)
    224 {
    225 	if (a > b)
    226 		return a;
    227 	return b;
    228 }
    229 
    230 /*
    231 	Creates an uneven grid by splitting the map recursively.
    232 	Returns an array containing the cells (rects) of the grid.
    233 */
    234 struct rect *
    235 generaterects(size_t heightmin, size_t widthmin, uint32_t prng)
    236 {
    237 	struct rect *queuehead, *queuetail;
    238 	struct rect *r, *t;
    239 	struct rect *rects;
    240 	size_t w, h;
    241 	int vertical, spaceforvertical, spaceforhorizontal;
    242 
    243 	r = malloc(sizeof(*r));
    244 	memset(r, 0, sizeof(*r));
    245 	r->x1 = r->y1 = 0;
    246 	r->x2 = MAPWIDTH;
    247 	r->y2 = MAPHEIGHT;
    248 	r->d = 0;
    249 
    250 	queuetail = r;
    251 	queuetail->next = NULL;
    252 	queuehead = r;
    253 
    254 	rects = NULL;
    255 
    256 	while (queuehead) {
    257 		r = queuehead;
    258 		if (queuetail == queuehead)
    259 			queuetail = NULL;
    260 		queuehead = queuehead->next;
    261 
    262 		spaceforvertical = r->y2 - r->y1 >= heightmin * 2;
    263 		spaceforhorizontal = r->x2 - r->x1 >= widthmin * 2;
    264 
    265 		if (spaceforhorizontal && spaceforvertical) {
    266 			vertical = ranqd1(&prng) & 1;
    267 		} else if (spaceforhorizontal) {
    268 			vertical = 0;
    269 		} else if (spaceforvertical) {
    270 			vertical = 1;
    271 		} else {
    272 			r->next = rects;
    273 			rects = r;
    274 			continue;
    275 		}
    276 
    277 		if (vertical) {
    278 			w = r->x2 - r->x1;
    279 			h = heightmin + ranqd1(&prng) % (1 + r->y2 - r->y1 - heightmin * 2);
    280 		} else {
    281 			w = widthmin + ranqd1(&prng) % (1 + r->x2 - r->x1 - widthmin * 2);
    282 			h = r->y2 - r->y1;
    283 		}
    284 
    285 		t = malloc(sizeof(*t));
    286 		memset(t, 0, sizeof(*t));
    287 		t->x1 = r->x1;
    288 		t->y1 = r->y1;
    289 		t->x2 = r->x1 + w;
    290 		t->y2 = r->y1 + h;
    291 		t->d = r->d + 1;
    292 
    293 		if (!queuetail) {
    294 			queuehead = t;
    295 			queuetail = t;
    296 		} else {
    297 			queuetail->next = t;
    298 			queuetail = t;
    299 		}
    300 
    301 		t = malloc(sizeof(*t));
    302 		memset(t, 0, sizeof(*t));
    303 		if (vertical) {
    304 			t->x1 = r->x1;
    305 			t->y1 = r->y1 + h;
    306 		} else {
    307 			t->x1 = r->x1 + w;
    308 			t->y1 = r->y1;
    309 		}
    310 		t->x2 = r->x2;
    311 		t->y2 = r->y2;
    312 		t->d = r->d + 1;
    313 
    314 		queuetail->next = t;
    315 		queuetail = t;
    316 
    317 		free(r);
    318 	}
    319 
    320 	return rects;
    321 }
    322 
    323 void
    324 connectpoints_horizontal(size_t y,
    325                        size_t ax, int ea, struct tile *at,
    326                        size_t bx, int eb, struct tile *bt,
    327                        struct tile *t)
    328 {
    329 	size_t i, s, e;
    330 	ssize_t ii;
    331 
    332 	if (ax < bx)
    333 		ii = 1;
    334 	else if (ax > bx)
    335 		ii = -1;
    336 	else
    337 		ii = 0;
    338 
    339 	s = ax;
    340 	if (ea)
    341 		s += ii;
    342 	e = bx + ii;
    343 	if (eb)
    344 		e -= ii;
    345 
    346 	for (i = s; i != e; i += ii)
    347 		map[y][i].tile = t;
    348 
    349 	if (e - ii == s) {
    350 		if (at != t)
    351 			map[y][s].tile = at;
    352 		if (bt != t)
    353 			map[y][s].tile = bt;
    354 	} else {
    355 		map[y][s].tile = at;
    356 		map[y][e - ii].tile = bt;
    357 	}
    358 }
    359 
    360 void
    361 connectpoints_vertical(size_t x,
    362                        size_t ay, int ea, struct tile *at,
    363                        size_t by, int eb, struct tile *bt,
    364                        struct tile *t)
    365 {
    366 	size_t i, s, e;
    367 	ssize_t ii;
    368 
    369 	if (ay < by)
    370 		ii = 1;
    371 	else if (ay > by)
    372 		ii = -1;
    373 	else
    374 		ii = 0;
    375 
    376 	s = ay;
    377 	if (ea)
    378 		s += ii;
    379 	e = by + ii;
    380 	if (eb)
    381 		e -= ii;
    382 
    383 	for (i = s; i != e; i += ii)
    384 		map[i][x].tile = t;
    385 
    386 	if (e - ii == s) {
    387 		if (at != t)
    388 			map[s][x].tile = at;
    389 		if (bt != t)
    390 			map[s][x].tile = bt;
    391 	} else {
    392 		map[s][x].tile = at;
    393 		map[e - ii][x].tile = bt;
    394 	}
    395 }
    396 
    397 void
    398 connectpoints(size_t ax, size_t ay, int ea, struct tile *at,
    399               size_t bx, size_t by, int eb, struct tile *bt,
    400               int vertical, struct tile *ct)
    401 {
    402 	if (!vertical) {
    403 		connectpoints_horizontal(ay,
    404 					 ax, ea, at,
    405 					 bx, 0, ct,
    406 					 ct);
    407 		connectpoints_vertical(bx,
    408 				       ay, 0, ct,
    409 				       by, eb, bt,
    410 				       ct);
    411 	} else {
    412 		connectpoints_vertical(ax,
    413 				       ay, ea, at,
    414 				       by, 0, ct,
    415 				       ct);
    416 		connectpoints_horizontal(by,
    417 					 ax, 0, ct,
    418 					 bx, eb, bt,
    419 					 ct);
    420 	}
    421 }
    422 
    423 void
    424 nearestpoints(struct room *a, struct room *b, size_t *ax, size_t *ay, size_t *bx, size_t *by)
    425 {
    426 	if (a->y >= b->y && a->y < b->y + b->h) {
    427 		*ay = *by = a->y;
    428 	} else if (b->y >= a->y && b->y < a->y + a->h) {
    429 		*ay = *by = b->y;
    430 	} else if (a->y >= b->y) {
    431 		*ay = a->y;
    432 		*by = b->y + b->h - 1;
    433 	} else if (b->y >= a->y) {
    434 		*ay = a->y + a->h - 1;
    435 		*by = b->y;
    436 	}
    437 
    438 	if (a->x >= b->x && a->x < b->x + b->w) {
    439 		*ax = *bx = a->x;
    440 	} else if (b->x >= a->x && b->x < a->x + a->w) {
    441 		*ax = *bx = b->x;
    442 	} else if (a->x >= b->x) {
    443 		*ax = a->x;
    444 		*bx = b->x + b->w - 1;
    445 	} else if (b->x >= a->x) {
    446 		*ax = a->x + a->w - 1;
    447 		*bx = b->x;
    448 	}
    449 }
    450 
    451 void
    452 connectadjacentrooms(struct rect *a, struct room *ar, struct rect *b, struct room *br)
    453 {
    454 	size_t irx1, iry1, irx2, iry2;
    455 	size_t rx1, ry1, rx2, ry2;
    456 	size_t cx, cy;
    457 	struct rect *r1, *r2;
    458 	struct room *room1, *room2;
    459 	int vertical;
    460 
    461 	if (a->x2 == b->x1) {
    462 		r1 = a;
    463 		room1 = ar;
    464 		r2 = b;
    465 		room2 = br;
    466 	} else if (b->x2 == a->x1) {
    467 		r1 = b;
    468 		room1 = br;
    469 		r2 = a;
    470 		room2 = ar;
    471 	} else if (a->y2 == b->y1) {
    472 		r1 = a;
    473 		room1 = ar;
    474 		r2 = b;
    475 		room2 = br;
    476 	} else if (b->y2 == a->y1) {
    477 		r1 = b;
    478 		room1 = br;
    479 		room2 = ar;
    480 		r2 = a;
    481 	} else {
    482 		return;
    483 	}
    484 
    485 	if (r1->y2 == r2->y1) {
    486 		irx1 = max(r1->x1, r2->x1);
    487 		irx2 = min(r1->x2, r2->x2);
    488 		iry1 = r1->y2;
    489 		iry2 = r1->y2 + 1;
    490 	} else {
    491 		iry1 = max(r1->y1, r2->y1);
    492 		iry2 = min(r1->y2, r2->y2);
    493 		irx1 = r1->x2;
    494 		irx2 = r1->x2 + 1;
    495 	}
    496 
    497 	nearestpoints(room1, room2, &rx1, &ry1, &rx2, &ry2);
    498 
    499 	if (r1->y2 == r2->y1) {
    500 		/* both points are in the intersection */
    501 		if (rx1 >= irx1 && rx1 < irx2 &&
    502 		    rx2 >= irx1 && rx2 < irx2) {
    503 			vertical = 1;
    504 			cx = (rx2 + rx1) / 2;
    505 			cy = (ry2 + ry1) / 2;
    506 		} else
    507 		/* none is in the intersection */
    508 		if (!(rx1 >= irx1 && rx1 < irx2) &&
    509 		    !(rx2 >= irx1 && rx2 < irx2)) {
    510 			vertical = 0;
    511 			cx = irx1;
    512 			cy = r1->y2;
    513 		} else if (rx1 >= irx1 && rx1 < irx2) {
    514 			vertical = 1;
    515 			cx = (rx2 + rx1) / 2;
    516 			cy = r1->y2;
    517 		} else if (rx2 >= irx1 && rx2 < irx2) {
    518 			vertical = 1;
    519 			cx = rx2;
    520 			cy = r1->y2 - 1;
    521 		}
    522 	} else {
    523 		/* both points are in the intersection */
    524 		if (ry1 >= iry1 && ry1 < iry2 &&
    525 		    ry2 >= iry1 && ry2 < iry2) {
    526 			vertical = 0;
    527 			cx = (rx2 + rx1) / 2;
    528 			cy = (ry2 + ry1) / 2;
    529 		} else
    530 		/* none is in the intersection */
    531 		if (!(ry1 >= iry1 && ry1 < iry2) &&
    532 		    !(ry2 >= iry1 && ry2 < iry2)) {
    533 			vertical = 1;
    534 			cx = r1->x2;
    535 			cy = iry1;
    536 		} else if (ry1 >= iry1 && ry1 < iry2) {
    537 			vertical = 0;
    538 			cx = r1->x2;
    539 			cy = (ry2 + ry1) / 2;
    540 		} else if (ry2 >= iry1 && ry2 < iry2) {
    541 			vertical = 0;
    542 			cx = r1->x2 - 1;
    543 			cy = ry2;
    544 		}
    545 	}
    546 
    547 	if (rx1 == rx2) {
    548 		connectpoints_vertical(rx1,
    549 		                       ry1, 1, &tile_door,
    550 		                       ry2, 1, &tile_door,
    551 		                       &tile_corridor);
    552 	} else if (ry1 == ry2) {
    553 		connectpoints_horizontal(ry1,
    554 		                         rx1, 1, &tile_door,
    555 		                         rx2, 1, &tile_door,
    556 		                         &tile_corridor);
    557 	} else {
    558 		connectpoints(rx1, ry1, 1, &tile_door,
    559 			      cx, cy, 0, &tile_corridor,
    560 			      vertical, &tile_corridor);
    561 		connectpoints(cx, cy, 1, &tile_corridor,
    562 			      rx2, ry2, 1, &tile_door,
    563 			      !vertical, &tile_corridor);
    564 	}
    565 }
    566 
    567 int
    568 rectisfull(struct rect *x, struct rect *r)
    569 {
    570 	return !!r->data.i;
    571 }
    572 
    573 int
    574 rectisempty(struct rect *x, struct rect *r)
    575 {
    576 	return !r->data.i;
    577 }
    578 
    579 int
    580 rectisnotp(struct rect *x, struct rect *r)
    581 {
    582 	return r->data.p && x->p != r && r->p != x;
    583 }
    584 
    585 int
    586 rectisrandom(struct rect *x, struct rect *r)
    587 {
    588 	return 1;
    589 }
    590 
    591 /*
    592 	Basically https://www.roguebasin.com/index.php/Diffusion-limited_aggregation
    593 	Returns the list of carved rooms.
    594 */
    595 struct rect *
    596 dla(struct rect *rects, size_t l, uint32_t prng) {
    597 	size_t rl, i, n;
    598 	struct rect *r, *t, *walk, *p;
    599 
    600 	for (r = rects, rl = 0; r; r = r->next)
    601 		rl++;
    602 
    603 	if (l > rl)
    604 		l = rl;
    605 
    606 	/* get the rect which contains the map center */
    607 	for (r = rects; r; r = r->next) {
    608 		if (MAPHEIGHT / 2 >= r->y1 && MAPHEIGHT / 2 < r->y2 &&
    609 		    MAPWIDTH / 2 >= r->x1 && MAPWIDTH / 2 < r->x2)
    610 			break;
    611 	}
    612 
    613 	p = NULL;
    614 	walk = NULL;
    615 	i = 0;
    616 	for (;;) {
    617 		r->p = p;
    618 		r->data.i = 1;
    619 		r->next2 = walk;
    620 		walk = r;
    621 
    622 		if (i >= l - 1)
    623 			break;
    624 
    625 		t = NULL;
    626 		for (r = rects, n = 0; r; r = r->next) {
    627 			if (r->data.i)
    628 				continue;
    629 			n++;
    630 			if (ranqd1(&prng) / (1. + UINT16_MAX) < 1. / n)
    631 				t = r;
    632 		}
    633 
    634 		/* there is no free rect left */
    635 		if (!t)
    636 			break;
    637 
    638 		/* do a random walk starting from t until the walk collides with a carved room (r) */
    639 		while ((r = randomneighbor(t, rects, &prng, rectisrandom)) && !r->data.i)
    640 			t = r;
    641 
    642 		p = r;
    643 		r = t;
    644 
    645 		i++;
    646 	}
    647 
    648 	return walk;
    649 }
    650 
    651 void
    652 rendermapchar(size_t i, size_t j) {
    653 	if (map[i][j].tile->flags & Standout)
    654 		putp(tiparm(enter_standout_mode));
    655 	putchar(map[i][j].tile->c);
    656 	if (map[i][j].tile->flags & Standout)
    657 		putp(tiparm(exit_standout_mode));
    658 }
    659 
    660 void
    661 rendermapline(size_t i)
    662 {
    663 	size_t j;
    664 
    665 	for (j = ox; j < min(MAPWIDTH, ox + columns); j++)
    666 		rendermapchar(i, j);
    667 }
    668 
    669 void
    670 rendermap(void)
    671 {
    672 	size_t i;
    673 
    674 	if (px < columns / 2 || MAPWIDTH <= columns)
    675 		ox = 0;
    676 	else if (px >= MAPWIDTH - columns / 2 - 1)
    677 		ox = MAPWIDTH - columns;
    678 	else
    679 		ox = px - columns / 2;
    680 
    681 	if (py < maplines / 2 || MAPHEIGHT <= maplines)
    682 		oy = 0;
    683 	else if (py >= MAPHEIGHT - maplines / 2 - 1)
    684 		oy = MAPHEIGHT - maplines;
    685 	else
    686 		oy = py - maplines / 2;
    687 
    688 	for (i = oy; i < min(MAPHEIGHT, oy + (lines - 2)); i++) {
    689 		if (i != oy)
    690 			putp(tiparm(cursor_down));
    691 		rendermapline(i);
    692 	}
    693 }
    694 
    695 size_t
    696 placeitems_hash(Item *item, size_t *assocs, size_t k)
    697 {
    698 	Dir *dir;
    699 	Item *citem;
    700 	size_t i;
    701 
    702 	dir = item->dat;
    703 	for (i = 0; i < dir->nitems; i++) {
    704 		citem = &dir->items[i];
    705 		/* TODO Somewhere else */
    706 		if (!citem->host || !citem->port || !citem->selector)
    707 			continue;
    708 		assocs[i] = fnv1a(6, item->host, item->port, item->selector, citem->host, citem->port, citem->selector) % k;
    709 	}
    710 
    711 	return k;
    712 }
    713 
    714 #define POSITIONS_LENGTH 5
    715 enum {
    716 	Portal,
    717 	StaircaseDown,
    718 	Bookshelf,
    719 	OtherStuff,
    720 	Back
    721 };
    722 
    723 enum {
    724 	FillEntireCell = 1
    725 };
    726 
    727 #define length(a) (sizeof(a) / sizeof(a[0]))
    728 static struct dungeontype {
    729 	char *name;
    730 	char flags;
    731 	size_t heightmin;
    732 	size_t heightmax;
    733 	size_t widthmin;
    734 	size_t widthmax;
    735 	size_t margin;
    736 	size_t wiggle;
    737 } dungeontypes[] = {
    738 	{ "rogueish", 0, 2, 5, 3, 7, 2, 0 },
    739 	{ "rogueish-wide", 0, 2, 5, 3, 7, 3, 1 },
    740 	{ "compact", FillEntireCell, 2, 2, 3, 3, 1, 0 },
    741 };
    742 
    743 void
    744 generatemap(Item *item, Item *pitem)
    745 {
    746 	Dir *dir;
    747 	Item *citem;
    748 	size_t l, i, j, k, ir, n, m, x, y;
    749 	struct rect *rects, *walk, *tr, *cr;
    750 	struct room *rooms, *room;
    751 	size_t *cassocs;
    752 	int changedlevel, gonedown;
    753 	char buffer[10];
    754 	uint32_t prng;
    755 	struct {
    756 		size_t x, y;
    757 	} positions[POSITIONS_LENGTH];
    758 	size_t cellwidth, cellheight;
    759 	struct dungeontype *type;
    760 
    761 	type = &dungeontypes[fnv1a(3, item->host, item->port, "dungeontype") % length(dungeontypes)];
    762 
    763 	cellheight = type->heightmin + 2 * type->margin + type->wiggle;
    764 	cellwidth = type->widthmin + 2 * type->margin + type->wiggle;
    765 
    766 	rects = generaterects(cellheight, cellwidth, fnv1a(4, item->host, item->port, item->selector, "gridseed"));
    767 
    768 	dir = item->dat;
    769 	for (j = l = 0; j < dir->nitems; j++) {
    770 		if (dir->items[j].type != 0 &&
    771 		    dir->items[j].type != 'i' &&
    772 		    dir->items[j].type != '3')
    773 			l++;
    774 	}
    775 
    776 	k = 1 + l / 10;
    777 	walk = dla(rects, k, fnv1a(4, item->host, item->port, item->selector, "randomwalkseed"));
    778 	for (cr = walk, k = 0; cr; cr = cr->next2, k++);
    779 
    780 	for (cr = rects; cr; cr = cr->next)
    781 		cr->data.p = NULL;
    782 
    783 	rooms = calloc(k, sizeof(*rooms));
    784 	for (cr = walk, i = 0; cr; cr = cr->next2, i++)
    785 		cr->data.p = &rooms[i];
    786 
    787 	prng = fnv1a(4, item->host, item->port, item->selector, "roomsseed");
    788 	for (cr = walk; cr; cr = cr->next2) {
    789 		room = cr->data.p;
    790 
    791 		if (type->flags & FillEntireCell) {
    792 			room->w = cr->x2 - cr->x1 - 2 * type->margin;
    793 			room->x = cr->x1 + type->margin;
    794 			room->h = cr->y2 - cr->y1 - 2 * type->margin;
    795 			room->y = cr->y1 + type->margin;
    796 		} else {
    797 			room->w = type->widthmin + ranqd1(&prng) % (1 + min(cr->x2 - cr->x1 - type->widthmin - 2 * type->margin, type->widthmax - type->widthmin));
    798 			room->x = cr->x1 + type->margin + ranqd1(&prng) % (1 + cr->x2 - cr->x1 - room->w - 2 * type->margin);
    799 			room->h = type->heightmin + ranqd1(&prng) % (1 + min(cr->y2 - cr->y1 - type->heightmin - 2 * type->margin, type->heightmax - type->heightmin));
    800 			room->y = cr->y1 + type->margin + ranqd1(&prng) % (1 + cr->y2 - cr->y1 - room->h - 2 * type->margin);
    801 		}
    802 	}
    803 
    804 	for (i = 0; i < MAPHEIGHT; i++) {
    805 		for (j = 0; j < MAPWIDTH; j++) {
    806 			map[i][j].tile = &tile_void;
    807 			free(map[i][j].items);
    808 			map[i][j].items = NULL;
    809 			map[i][j].nitems = 0;
    810 		}
    811 	}
    812 
    813 	for (cr = walk; cr; cr = cr->next2) {
    814 		room = cr->data.p;
    815 
    816 		for (x = room->x - 1; x < room->x + room->w + 1; x++)
    817 			map[room->y-1][x].tile = &tile_horizontalwall;
    818 		for (y = room->y; y < room->y + room->h; y++) {
    819 			map[y][room->x - 1].tile = &tile_verticalwall;
    820 			for (x = room->x; x < room->x + room->w; x++)
    821 				map[y][x].tile = &tile_floor;
    822 			map[y][room->x + room->w].tile = &tile_verticalwall;
    823 		}
    824 		for (x = room->x - 1; x < room->x + room->w + 1; x++)
    825 			map[room->y + room->h][x].tile = &tile_horizontalwall;
    826 	}
    827 
    828 	for (cr = walk; cr; cr = cr->next2) {
    829 		if (cr->p)
    830 			connectadjacentrooms(cr, cr->data.p,
    831 			                     cr->p, cr->p->data.p);
    832 
    833 		/* Add some loop possibility */
    834 		if (tr = randomneighbor(cr, rects, &prng, rectisnotp))
    835 			connectadjacentrooms(cr, cr->data.p,
    836 			                     tr, tr->data.p);
    837 	}
    838 
    839 	cassocs = calloc(dir->nitems, sizeof(*cassocs));
    840 
    841 	k = placeitems_hash(item, cassocs, k);
    842 
    843 	changedlevel = item != pitem;
    844 	gonedown = pitem == item->entry;
    845 
    846 	/*
    847 		Insert items
    848                 The placement of items affects the initial placement of the
    849                 player, because they could have gone back to this map, so they
    850                 should appear at the elevator/portal/stair they used.
    851 	*/
    852 
    853 	/*
    854                 The initial room is everytime the first one. Reason: The count
    855                 of rooms is based on how many entries are in the gophermap and
    856                 how many rooms can fit on the map. There will be at minimum 1.
    857                 So when more entries get added there will be more rooms but the
    858                 first one stays at the same position. I think about the
    859                 retrying and clownflare things on bitreich.org, the selector
    860                 doesn't change...
    861 	*/
    862 	ir = 0;
    863 
    864 	for (i = 0; i < k; i++) {
    865 		/* select random positions for different item types inside the current room */
    866 		snprintf(buffer, sizeof(buffer), "%lu", i);
    867 		prng = fnv1a(4, item->host, item->port, item->selector, buffer);
    868 		for (j = 0, m = rooms[i].h * rooms[i].w; j < m; j++) {
    869 			n = j;
    870 			if (j >= POSITIONS_LENGTH)
    871 				n *= ranqd1(&prng) / (double)UINT16_MAX;
    872 
    873 			if (n < POSITIONS_LENGTH) {
    874 				positions[n].x = rooms[i].x + j % rooms[i].w;
    875 				positions[n].y = rooms[i].y + j / rooms[i].w;
    876 			}
    877 		}
    878 
    879 		for (j = 0; j < dir->nitems; j++) {
    880 			if (cassocs[j] != i)
    881 				continue;
    882 
    883 			citem = &dir->items[j];
    884 			switch (citem->type) {
    885 			case '0':
    886 				x = positions[Bookshelf].x;
    887 				y = positions[Bookshelf].y;
    888 				if (map[y][x].nitems)
    889 					map[y][x].tile = &tile_bookshelf;
    890 				else
    891 					map[y][x].tile = &tile_book;
    892 				break;
    893 			case '1':
    894 				if (strcmp(citem->host, item->host) || strcmp(citem->port, item->port)) {
    895 					x = positions[Portal].x;
    896 					y = positions[Portal].y;
    897 					if (map[y][x].nitems)
    898 						map[y][x].tile = &tile_portalmachine;
    899 					else
    900 						map[y][x].tile = &tile_portal;
    901 				} else {
    902 					x = positions[StaircaseDown].x;
    903 					y = positions[StaircaseDown].y;
    904 					if (map[y][x].nitems)
    905 						map[y][x].tile = &tile_elevator;
    906 					else
    907 						map[y][x].tile = &tile_stairsdown;
    908 				}
    909 				break;
    910 			case 0:
    911 			case 'i':
    912 			case '3':
    913 				continue;
    914 				break;
    915 			default:
    916 				x = positions[OtherStuff].x;
    917 				y = positions[OtherStuff].y;
    918 				map[y][x].tile = &tile_heapofstuff;
    919 				break;
    920 			}
    921 
    922 			map[y][x].nitems++;
    923 			map[y][x].items = realloc(map[y][x].items, map[y][x].nitems * sizeof(*map[y][x].items));
    924 			map[y][x].items[map[y][x].nitems-1] = citem;
    925 
    926 			if (changedlevel && citem == pitem) {
    927 				px = x;
    928 				py = y;
    929 			}
    930 		}
    931 
    932 		if (i == ir && item->entry != item) {
    933 			y = positions[Back].y;
    934 			x = positions[Back].x;
    935 			if (strcmp(item->entry->host, item->host) || strcmp(item->entry->port, item->port))
    936 				map[y][x].tile = &tile_backportal;
    937 			else
    938 				map[y][x].tile = &tile_stairsup;
    939 			map[y][x].nitems++;
    940 			map[y][x].items = realloc(map[y][x].items, map[y][x].nitems * sizeof(*map[y][x].items));
    941 			map[y][x].items[map[y][x].nitems-1] = item->entry;
    942 		}
    943 
    944 		if (changedlevel && i == ir && (gonedown || !pitem)) {
    945 			px = positions[Back].x;
    946 			py = positions[Back].y;
    947 		}
    948 	}
    949 
    950 	free(cassocs);
    951 	free(rooms);
    952 
    953 	for (cr = rects; cr;) {
    954 		tr = cr;
    955 		cr = cr->next;
    956 		free(tr);
    957 	}
    958 }
    959 
    960 void
    961 uisetup(void)
    962 {
    963 	tcgetattr(0, &tsave);
    964 	tsacc = tsave;
    965 	tsacc.c_lflag &= ~(ECHO|ICANON);
    966 	tsacc.c_cc[VMIN] = 1;
    967 	tsacc.c_cc[VTIME] = 0;
    968 	tcsetattr(0, TCSANOW, &tsacc);
    969 
    970 	if (termset != OK)
    971 		/* setupterm call exits on error */
    972 		termset = setupterm(NULL, 1, NULL);
    973 	putp(tiparm(clear_screen));
    974 	fflush(stdout);
    975 }
    976 
    977 void
    978 uicleanup(void)
    979 {
    980 	tcsetattr(0, TCSANOW, &tsave);
    981 
    982 	if (termset != OK)
    983 		return;
    984 
    985 	putp(tiparm(change_scroll_region, 0, lines-1));
    986 	putp(tiparm(clear_screen));
    987 	fflush(stdout);
    988 }
    989 
    990 char *
    991 uiprompt(char *fmt, ...)
    992 {
    993 	va_list ap;
    994 	char *input = NULL;
    995 	size_t n;
    996 	ssize_t r;
    997 
    998 	putp(tiparm(save_cursor));
    999 
   1000 	putp(tiparm(cursor_address, lines-1, 0));
   1001 	putp(tiparm(clr_eol));
   1002 
   1003 	va_start(ap, fmt);
   1004 	vsnprintf(bufout, sizeof(bufout), fmt, ap);
   1005 	va_end(ap);
   1006 
   1007 	n = mbsprint(bufout, columns);
   1008 
   1009 	putp(tiparm(clr_eol));
   1010 
   1011 	putp(tiparm(cursor_address, lines-1, n));
   1012 
   1013 	tsacc.c_lflag |= (ECHO|ICANON);
   1014 	tcsetattr(0, TCSANOW, &tsacc);
   1015 	fflush(stdout);
   1016 
   1017 	n = 0;
   1018 	r = getline(&input, &n, stdin);
   1019 
   1020 	tsacc.c_lflag &= ~(ECHO|ICANON);
   1021 	tcsetattr(0, TCSANOW, &tsacc);
   1022 	putp(tiparm(restore_cursor));
   1023 	fflush(stdout);
   1024 
   1025 	if (r == -1 || feof(stdin)) {
   1026 		clearerr(stdin);
   1027 		clear(&input);
   1028 	} else if (input[r - 1] == '\n') {
   1029 		input[--r] = '\0';
   1030 	}
   1031 
   1032 	return input;
   1033 }
   1034 
   1035 void
   1036 displaybar(char *s) {
   1037 	size_t n;
   1038 
   1039 	putp(tiparm(save_cursor));
   1040 
   1041 	putp(tiparm(cursor_address, lines-2, 0));
   1042 	putp(tiparm(enter_standout_mode));
   1043 
   1044 	n = mbsprint(s, columns);
   1045 	for (n = columns - n; n; n--)
   1046 		putchar(' ');
   1047 
   1048 	putp(tiparm(exit_standout_mode));
   1049 
   1050 	putp(tiparm(restore_cursor));
   1051 	fflush(stdout);
   1052 }
   1053 
   1054 void
   1055 vdisplayinfoline(char *fmt, va_list ap)
   1056 {
   1057 	putp(tiparm(save_cursor));
   1058 
   1059 	putp(tiparm(cursor_address, lines-1, 0));
   1060 
   1061 	vsnprintf(bufout, sizeof(bufout), fmt, ap);
   1062 
   1063 	mbsprint(bufout, columns);
   1064 
   1065 	putp(tiparm(clr_eol));
   1066 
   1067 	putp(tiparm(restore_cursor));
   1068 	fflush(stdout);
   1069 }
   1070 
   1071 void
   1072 uistatus(char *fmt, ...)
   1073 {
   1074 	va_list ap;
   1075 	size_t n;
   1076 
   1077 	putp(tiparm(save_cursor));
   1078 
   1079 	putp(tiparm(cursor_address, lines-1, 0));
   1080 
   1081 	va_start(ap, fmt);
   1082 	n = vsnprintf(bufout, sizeof(bufout), fmt, ap);
   1083 	va_end(ap);
   1084 
   1085 	if (n < sizeof(bufout)-1) {
   1086 		snprintf(bufout+n, sizeof(bufout)-n,
   1087 		         " [Press a key to continue \xe2\x98\x83]");
   1088 	}
   1089 
   1090 	mbsprint(bufout, columns);
   1091 
   1092 	putp(tiparm(clr_eol));
   1093 
   1094 	putp(tiparm(restore_cursor));
   1095 	fflush(stdout);
   1096 
   1097 	mygetchar();
   1098 }
   1099 
   1100 void
   1101 displayinfoline(char *fmt, ...)
   1102 {
   1103 	va_list ap;
   1104 
   1105 	va_start(ap, fmt);
   1106 	vdisplayinfoline(fmt, ap);
   1107 	va_end(ap);
   1108 }
   1109 
   1110 char *menutitle;
   1111 Item **menuitems;
   1112 size_t menunitems;
   1113 volatile size_t menuoffset;
   1114 size_t menuselected;
   1115 
   1116 void
   1117 menudraw(void)
   1118 {
   1119 	size_t i, n;
   1120 
   1121 	putp(tiparm(change_scroll_region, 1, lines-1));
   1122 	putp(tiparm(clear_screen));
   1123 
   1124 	putp(tiparm(enter_standout_mode));
   1125 	puts(menutitle);
   1126 	putp(tiparm(exit_standout_mode));
   1127 
   1128 	if (menuselected - menuoffset >= lines - 1)
   1129 		menuoffset = menuselected - (lines - 1) + 1;
   1130 
   1131 	for (i = menuoffset, n = 0; i < menunitems && n < lines - 1; i++, n++) {
   1132 		if (i != menuoffset)
   1133 			putp(tiparm(cursor_down));
   1134 		if (i == menuselected)
   1135 			putp(tiparm(enter_standout_mode));
   1136 		mbsprint(menuitems[i]->username, columns);
   1137 		if (i == menuselected)
   1138 			putp(tiparm(exit_standout_mode));
   1139 		putp(tiparm(column_address, 0));
   1140 	}
   1141 	fflush(stdout);
   1142 }
   1143 
   1144 Item *
   1145 showmenu(char *title, Item **item, size_t l)
   1146 {
   1147 	Item *selection;
   1148 
   1149 	menutitle = title;
   1150 	menuitems = item;
   1151 	menunitems = l;
   1152 	menuselected = 0;
   1153 	menuoffset = 0;
   1154 	screen = MenuScreen;
   1155 	drawscreen();
   1156 
   1157 	selection = NULL;
   1158 	for (;;) {
   1159 		switch (mygetchar()) {
   1160 		case 'j':
   1161 			if (menuselected + 1 < menunitems) {
   1162 				putp(tiparm(cursor_address, 1 + menuselected - menuoffset, 0));
   1163 				mbsprint(menuitems[menuselected]->username, columns);
   1164 				menuselected++;
   1165 				putp(tiparm(column_address, 0));
   1166 				if (menuselected - menuoffset >= lines - 1) {
   1167 					menuoffset++;
   1168 					putp(tiparm(scroll_forward));
   1169 				} else {
   1170 					putp(tiparm(cursor_down));
   1171 				}
   1172 				putp(tiparm(enter_standout_mode));
   1173 				mbsprint(menuitems[menuselected]->username, columns);
   1174 				putp(tiparm(exit_standout_mode));
   1175 			}
   1176 			break;
   1177 		case 'k':
   1178 			if (menuselected > 0) {
   1179 				putp(tiparm(cursor_address, 1 + menuselected - menuoffset, 0));
   1180 				mbsprint(menuitems[menuselected]->username, columns);
   1181 				menuselected--;
   1182 				putp(tiparm(column_address, 0));
   1183 				if (menuselected < menuoffset) {
   1184 					menuoffset = menuselected;
   1185 					putp(tiparm(scroll_reverse));
   1186 				} else {
   1187 					putp(tiparm(cursor_up));
   1188 				}
   1189 				putp(tiparm(enter_standout_mode));
   1190 				mbsprint(menuitems[menuselected]->username, columns);
   1191 				putp(tiparm(exit_standout_mode));
   1192 			}
   1193 			break;
   1194 		case ' ':
   1195 			selection = menuitems[menuselected];
   1196 		case 0x1b:
   1197 			goto endloop;
   1198 			break;
   1199 		}
   1200 		fflush(stdout);
   1201 	}
   1202 
   1203 endloop:
   1204 	screen = DungeonScreen;
   1205 	drawscreen();
   1206 
   1207 	return selection;
   1208 }
   1209 
   1210 Item *
   1211 interactitem(struct cell *c)
   1212 {
   1213 	displayinfoline(map[py][px].tile->afterinteract);
   1214 
   1215 	return map[py][px].items[0];
   1216 }
   1217 
   1218 Item *
   1219 interactmenu(struct cell *c)
   1220 {
   1221 	Item *selection;
   1222 
   1223 	if (selection = showmenu(map[py][px].tile->name, map[py][px].items, map[py][px].nitems))
   1224 		displayinfoline(map[py][px].tile->afterinteract);
   1225 
   1226 	return selection;
   1227 }
   1228 
   1229 Item *
   1230 interact(Item *item)
   1231 {
   1232 	if (map[py][px].tile->interact)
   1233 		return map[py][px].tile->interact(&map[py][px]);
   1234 
   1235 	return NULL;
   1236 }
   1237 
   1238 void
   1239 describe(size_t x, size_t y, int verbose)
   1240 {
   1241 	char *name;
   1242 
   1243 	if (map[y][x].nitems) {
   1244 		if (*map[y][x].items[0]->username) {
   1245 			name = map[y][x].items[0]->username;
   1246 		} else {
   1247 			itemuri(map[y][x].items[0], bufout2, sizeof(bufout2));
   1248 			name = bufout2;
   1249 		}
   1250 	} else {
   1251 		name = NULL;
   1252 	}
   1253 	if (map[y][x].tile->flags & Important || verbose)
   1254 		displayinfoline(map[y][x].tile->description, name);
   1255 	else
   1256 		displayinfoline("");
   1257 }
   1258 
   1259 void
   1260 dungeondraw(void)
   1261 {
   1262 	putp(tiparm(change_scroll_region, 0, lines-3));
   1263 	putp(tiparm(clear_screen));
   1264 
   1265 	rendermap();
   1266 
   1267 	putp(tiparm(cursor_address, py - oy, px - ox));
   1268 	putchar('@');
   1269 	putp(tiparm(cursor_address, py - oy, px - ox));
   1270 
   1271 	if (curentry->entry != curentry) {
   1272 		displaybar(curentry->username);
   1273 	} else {
   1274 		itemuri(curentry, bufout, sizeof(bufout));
   1275 		displaybar(bufout);
   1276 	}
   1277 
   1278 	describe(px, py, 0);
   1279 }
   1280 
   1281 void
   1282 move(ssize_t dx, ssize_t dy)
   1283 {
   1284 	ssize_t i;
   1285 	size_t x, y;
   1286 	size_t noy, nox;
   1287 
   1288 	if ((ssize_t)py + dy >= MAPHEIGHT || (ssize_t)py + dy < 0)
   1289 		return;
   1290 	if ((ssize_t)px + dx >= MAPWIDTH || (ssize_t)px + dx < 0)
   1291 		return;
   1292 
   1293 	x = px + dx;
   1294 	y = py + dy;
   1295 
   1296 	if (map[y][x].tile->flags & Blocks)
   1297 		return;
   1298 
   1299 	if (dx) {
   1300 		if (x < columns / 2 || MAPWIDTH <= columns)
   1301 			nox = 0;
   1302 		else if (x >= MAPWIDTH - columns / 2 - 1)
   1303 			nox = MAPWIDTH - columns;
   1304 		else
   1305 			nox = x - columns / 2;
   1306 
   1307 		if (ox != nox) {
   1308 			putp(tiparm(cursor_address, 0, 0));
   1309 			rendermap();
   1310 		} else {
   1311 			putp(tiparm(cursor_address, py - oy, px - ox));
   1312 			rendermapchar(py, px);
   1313 		}
   1314 	} else if (dy) {
   1315 		putp(tiparm(cursor_address, py - oy, px - ox));
   1316 		rendermapchar(py, px);
   1317 
   1318 		if (y < maplines / 2 || MAPHEIGHT <= maplines) {
   1319 			noy = 0;
   1320 		} else if (y >= MAPHEIGHT - maplines / 2 - 1) {
   1321 			noy = MAPHEIGHT - maplines;
   1322 		} else {
   1323 			noy = y - maplines / 2;
   1324 		}
   1325 
   1326 		if (noy < oy) {
   1327 			putp(tiparm(cursor_address, 0, 0));
   1328 			for (i = (ssize_t)oy - 1; i >= (ssize_t)noy; i--) {
   1329 				putp(tiparm(scroll_reverse));
   1330 				rendermapline(i);
   1331 				putp(tiparm(column_address, 0));
   1332 			}
   1333 		} else if (noy > oy) {
   1334 			putp(tiparm(cursor_address, lines-3, 0));
   1335 			for (i = oy + 1; i <= noy; i++) {
   1336 				putp(tiparm(scroll_forward));
   1337 				rendermapline(i + maplines - 1);
   1338 				putp(tiparm(column_address, 0));
   1339 			}
   1340 		}
   1341 		oy = noy;
   1342 	}
   1343 
   1344 	py = y;
   1345 	px = x;
   1346 	putp(tiparm(cursor_address, py - oy, px - ox));
   1347 	putchar('@');
   1348 	putp(tiparm(cursor_address, py - oy, px - ox));
   1349 
   1350 	describe(px, py, 0);
   1351 }
   1352 
   1353 void
   1354 drawscreen(void)
   1355 {
   1356 	switch (screen) {
   1357 	case DungeonScreen:
   1358 		dungeondraw();
   1359 		break;
   1360 	case MenuScreen:
   1361 		menudraw();
   1362 		break;
   1363 	}
   1364 	fflush(stdout);
   1365 }
   1366 
   1367 void
   1368 uidisplay(Item *entry)
   1369 {
   1370 	if (!entry || !(entry->type == '1' || entry->type == '+' || entry->type == '7'))
   1371 		return;
   1372 
   1373 	if (entry != curentry) {
   1374 		generatemap(entry, curentry);
   1375 		curentry = entry;
   1376 	}
   1377 
   1378 	drawscreen();
   1379 }
   1380 
   1381 Item *
   1382 uiselectitem(Item *entry)
   1383 {
   1384 	Item *e;
   1385 
   1386 	if (!entry || !entry->dat)
   1387 		return NULL;
   1388 
   1389 	for (;;) {
   1390 		switch (mygetchar()) {
   1391 		case 'h':
   1392 			move(-1, 0);
   1393 			break;
   1394 		case 'j':
   1395 			move(0, 1);
   1396 			break;
   1397 		case 'k':
   1398 			move(0, -1);
   1399 			break;
   1400 		case 'l':
   1401 			move(1, 0);
   1402 			break;
   1403 		case ' ':
   1404 			if (e = interact(entry))
   1405 				return e;
   1406 			break;
   1407 		case 'q':
   1408 		case 0x1b:
   1409 			return NULL;
   1410 		}
   1411 		fflush(stdout);
   1412 	}
   1413 }
   1414 
   1415 void
   1416 uisigwinch(int signal)
   1417 {
   1418 	sigwinch = 1;
   1419 }