sim

the sim text editor
git clone git://ssnf.xyz/sim
Log | Files | Refs | README

sim.c (20528B)


      1 #include <stdio.h>
      2 #include <stdarg.h>
      3 #include <stdlib.h>
      4 #include <string.h>
      5 
      6 #include "sim.h"
      7 
      8 #define MINSIZE 16
      9 #define MAXEMPTY 256
     10 #define FILECOUNT 8
     11 #define LENGTH(a) (sizeof(a)/sizeof(a[0]))
     12 
     13 typedef long Posn;
     14 
     15 enum {
     16 	Up = 1,
     17 	Down,
     18 	Left,
     19 	Right,
     20 	HalfUp,
     21 	HalfDown,
     22 	Top,
     23 	Bottom,
     24 	Letter,
     25 	Word,
     26 	EndWord,
     27 	PrevWord,
     28 	Till,
     29 	Line,
     30 	StartLine,
     31 	EndLine,
     32 	Insert,
     33 	Delete,
     34 	Change,
     35 	Ctrl = -0x60,
     36 	Esc = 0x1b,
     37 	Del = 0x7f
     38 };
     39 
     40 typedef struct {
     41 	Posn p0, p1;
     42 } Address;
     43 
     44 typedef struct {
     45 	char* s;
     46 	ulong n; /*number of filled characters*/
     47 	ulong size;
     48 } String;
     49 
     50 typedef struct {
     51 	String is[2048];
     52 	String ds[2048];
     53 	uint   c[2048];
     54 	uint   arg[2048];
     55 	uint   count[2048];
     56 	Posn   p0[2048];
     57 	short  n;
     58 } Buffer;
     59 
     60 typedef struct {
     61 	String* s;
     62 	String* name;
     63 	Address dot;
     64 	short   i;   /*buffer index*/
     65 	uchar   dirty;
     66 } File;
     67 
     68 typedef struct {
     69 	uchar key;
     70 	void (*func)(int);
     71 	int value;
     72 } Key;
     73 
     74 typedef struct {
     75 	Address* a;
     76 	ushort dot;
     77 	ushort n;
     78 	ushort size;
     79 } Frame;
     80 
     81 void        die(char*, ...);
     82 void*       emalloc(ulong);
     83 void*       erealloc(void*, ulong);
     84 static void blind_reader(Frame* fr, String* s, Posn p0);
     85 static void blind_writer(String* s, Frame* fr, ushort line, ushort offset, ushort top, ushort bot);
     86 static void buf_add(Buffer* b, String* is, String* ds, uint c, uint arg, uint count, Posn p0);
     87 static void change(int arg);
     88 static void count(int arg);
     89 static void delete(int arg);
     90 static void dot(int arg);
     91 static void redo(int arg);
     92 static void escape(int c);
     93 static void init();
     94 static void input(String* s, ushort line, char* msg);
     95 static void insert(int arg);
     96 static int  isword(uchar c);
     97 static void file_close(int arg);
     98 static void file_init(File* f);
     99 static void file_load(File* f);
    100 static void file_open(int c);
    101 static void file_save(int arg);
    102 static void fr_add(Frame* fr, Posn p0, Posn p1);
    103 static void fr_close(Frame* fr);
    104 static void fr_init(Frame* fr);
    105 static void fr_insert(Frame* p, Frame* q, ushort n);
    106 static void fr_insure(Frame* fr, ushort n);
    107 static void fr_update();
    108 static void fr_zero(Frame*);
    109 static void gmove(int arg);
    110 static void paste(int arg);
    111 static void pline();
    112 static void resize();
    113 static void search(int arg);
    114 static int  selection(int arg);
    115 static void str_init(String* p);
    116 static void str_close(String* p);
    117 static void str_zero(String* p);
    118 static void str_insure(String* p, ulong n);
    119 static void str_addc(String* p, int c);
    120 static void str_adds(String* p, char* s, ulong n);
    121 static void str_delc(String* p);
    122 static void str_insert(String* p, String* q, Posn p0);
    123 static void str_delete(String* p, Posn p0, Posn p1);
    124 static void undo(int arg);
    125 static void move(int);
    126 static void quit(int);
    127 static void yank(int arg);
    128 
    129 static Frame*  fr, frame[FILECOUNT];
    130 static File*   f, file[FILECOUNT];
    131 static Buffer* buf, buffer[FILECOUNT];
    132 static String  istr, srch;
    133 static Window  w;
    134 static uint    counter;
    135 
    136 #include "config.h"
    137 
    138 void
    139 die(char* fmt, ...)
    140 {
    141 	uint i;
    142 	va_list ap;
    143 
    144 	win_end();
    145 	va_start(ap, fmt);
    146 	vfprintf(stderr, fmt, ap);
    147 	va_end(ap);
    148 	if (fmt[0] && fmt[strlen(fmt) - 1] == ':')
    149 		perror(NULL);
    150 	else
    151 		fputc('\n', stderr);
    152 	for (i = 0; i < FILECOUNT; ++i) {
    153 		if (!file[i].s->n)
    154 			continue;
    155 		str_adds(file[i].name, ".swap", 5);
    156 		file_save(i);
    157 		fprintf(stderr, "file saved to %s\n", file[i].name->s);
    158 	}
    159 	*fmt = *(char*)NULL;
    160 }
    161 
    162 void*
    163 emalloc(ulong n)
    164 {
    165 	void* p;
    166 
    167 	p = malloc(n);
    168 	if (!p)
    169 		die("malloc:");
    170 	memset(p, 0, n);
    171 	return p;
    172 }
    173 
    174 void*
    175 erealloc(void* p, ulong n)
    176 {
    177 	p = realloc(p, n);
    178 	if (!p)
    179 		die("realloc:");
    180 	return p;
    181 }
    182 
    183 static void
    184 blind_reader(Frame* fr, String* s, Posn p0)
    185 {
    186 	Posn p1;
    187 	uint wx;
    188 
    189 	p1 = p0;
    190 	do {
    191 		for (wx = 0; wx < w.wx && p1 < s->n;) {
    192 			switch (s->s[p1]) {
    193 				case '\t':
    194 					wx += w.t - (wx % w.t);
    195 					break;
    196 				case '\n':
    197 					goto endloop;
    198 				default:
    199 					++wx;
    200 			}
    201 			if (wx < w.wx)
    202 				++p1;
    203 			if (wx >= w.wx) {
    204 				if (s->s[p1] == '\t')
    205 					++p1;
    206 				if (s->s[p1 + 1] == '\n')
    207 					++p1;
    208 			}
    209 		}
    210 		endloop:
    211 		fr_add(fr, p0, p1);
    212 		p0 = ++p1;
    213 	} while (p1 <= s->n && s->s[p1 - 1] != '\n');
    214 }
    215 
    216 static void
    217 blind_writer(String* s, Frame* fr, ushort line, ushort offset, ushort top, ushort bot)
    218 {
    219 	ushort i, o;
    220 
    221 	i = o = 0;
    222 	if (offset >= top)
    223 		i = offset - top + 1;
    224 	if (fr->n - offset > bot)
    225 		o = offset + bot - 1;
    226 	else if (fr->n)
    227 		o = fr->n - 1;
    228 	line = offset > line ? 0 : line - offset;
    229 	printf(CSI "%uH", line);
    230 	fwrite(&s->s[fr->a[i].p0], fr->a[o].p1 - fr->a[i].p0, 1, stdout);
    231 }
    232 
    233 static void
    234 buf_add(Buffer* b, String* is, String* ds, uint c, uint arg, uint count, Posn p0)
    235 {
    236 	b->n = ++f->i + 1;
    237 	str_zero(&b->is[f->i]);
    238 	str_zero(&b->ds[f->i]);
    239 	if (is != NULL)
    240 		str_insert(&b->is[f->i], is, 0);
    241 	if (ds != NULL)
    242 		str_insert(&b->ds[f->i], ds, 0);
    243 	b->c[f->i] = c;
    244 	b->arg[f->i] = arg;
    245 	b->count[f->i] = count;
    246 	b->p0[f->i] = p0;
    247 	f->dirty = '*';
    248 	fr_zero(fr);
    249 }
    250 
    251 static void
    252 change(int arg)
    253 {
    254 	String s;
    255 	uint   count;
    256 
    257 	if (!arg)
    258 		arg = fgetc(stdin);
    259 	switch (arg) {
    260 		case 'x': arg = Letter; break;
    261 		case 'c': arg = Line; break;
    262 		case 'G': arg = Bottom; break;
    263 		case 'g': arg = Top; break;
    264 		case 'w': arg = Word; break;
    265 		case 't': arg = Till; break;
    266 	}
    267 	count = counter;
    268 	arg = selection(arg);
    269 	if (arg == Word || arg == Letter || arg > 0x7f)
    270 		++f->dot.p1;
    271 	str_init(&s);
    272 	if (f->dot.p0 != f->dot.p1) {
    273 		str_adds(&s, f->s->s + f->dot.p0, f->dot.p1 - f->dot.p0);
    274 		str_delete(f->s, f->dot.p0, f->dot.p1);
    275 	}
    276 	f->dot.p0 = f->dot.p1 = f->dot.p0;
    277 	fr_zero(fr);
    278 	insert(0);
    279 	if (s.n)
    280 		str_insert(&buf->ds[f->i], &s, 0);
    281 	str_close(&s);
    282 	buf->c[f->i] = Change;
    283 	buf->arg[f->i] = arg;
    284 	buf->count[f->i] = count;
    285 	fr_update();
    286 }
    287 
    288 static void
    289 count(int arg)
    290 {
    291 	if (!counter && !arg) {
    292 		move(StartLine);
    293 		return;
    294 	}
    295 	counter *= 10;
    296 	counter += arg;
    297 }
    298 
    299 static void
    300 delete(int arg)
    301 {
    302 	String s;
    303 	uint   count;
    304 
    305 	if (!f->s->n)
    306 		return;
    307 	if (!arg) {
    308 		switch (arg = fgetc(stdin)) {
    309 			case 'x': arg = Letter; break;
    310 			case 'd': arg = Line; break;
    311 			case 'G': arg = Bottom; break;
    312 			case 'g': arg = Top; break;
    313 			case 'w': arg = Word; break;
    314 			case 't': arg = Till; break;
    315 			default:
    316 				return;
    317 		}
    318 	}
    319 	count = counter;
    320 	if ((arg = selection(arg)) < 0)
    321 		return;
    322 	str_init(&s);
    323 	str_zero(&istr);
    324 	str_adds(&s, f->s->s + f->dot.p0, f->dot.p1 + 1 - f->dot.p0);
    325 	str_adds(&istr, f->s->s + f->dot.p0, f->dot.p1 + 1 - f->dot.p0);
    326 	buf_add(buf, NULL, &s, Delete, arg, count, f->dot.p0);
    327 	str_delete(f->s, f->dot.p0, f->dot.p1 + 1);
    328 	str_close(&s);
    329 	if (f->dot.p0 == f->s->n && f->dot.p0)
    330 		--f->dot.p0;
    331 	f->dot.p1 = f->dot.p0;
    332 	fr_update();
    333 }
    334 
    335 static void
    336 dot(int arg)
    337 {
    338 	String ds;
    339 
    340 	if (f->i < 0)
    341 		f->i = 0;
    342 	arg = buf->arg[f->i];
    343 	counter = buf->count[f->i];
    344 	switch (buf->c[f->i]) {
    345 		case Insert:
    346 			if (arg == Down)
    347 				move(EndLine);
    348 			else
    349 				move(arg);
    350 			str_insert(f->s, &istr, f->dot.p0);
    351 			buf_add(buf, &istr, NULL, Insert, arg, counter, f->dot.p0);
    352 			break;
    353 		case Delete:
    354 			delete(arg);
    355 			break;
    356 		case Change:
    357 			str_init(&ds);
    358 			if (buf->ds[f->i].n) {
    359 				selection(arg);
    360 				if (arg == Word || arg == Letter || arg > 0x7f)
    361 					++f->dot.p1;
    362 				str_adds(&ds, f->s->s + f->dot.p0, f->dot.p1 - f->dot.p0);
    363 				str_delete(f->s, f->dot.p0, f->dot.p1);
    364 			}
    365 			if (buf->is[f->i].n)
    366 				str_insert(f->s, &buf->is[f->i], f->dot.p0);
    367 			buf_add(buf, &buf->is[f->i], &ds, Change, arg, buf->count[f->i], f->dot.p0);
    368 			str_close(&ds);
    369 			f->dot.p1 = f->dot.p0;
    370 			break;
    371 	}
    372 	fr_update();
    373 }
    374 
    375 static void
    376 redo(int arg)
    377 {
    378 	if (f->i == buf->n - 1)
    379 		return;
    380 	if (buf->ds[++f->i].n)
    381 		str_delete(f->s, buf->p0[f->i], buf->p0[f->i] + buf->ds[f->i].n);
    382 	if (buf->is[f->i].n)
    383 		str_insert(f->s, &buf->is[f->i], buf->p0[f->i]);
    384 	f->dot.p0 = f->dot.p1 = buf->p0[f->i];
    385 	f->dirty = '*';
    386 	fr_zero(fr);
    387 	fr_update();
    388 }
    389 
    390 static void
    391 escape(int c)
    392 {
    393 	counter = 0;
    394 	c = fgetc(stdin) - 0x30;
    395 	if (c > 0 && c <= 8) {
    396 		--c;
    397 		f = &file[c];
    398 		fr = &frame[c];
    399 		buf = &buffer[c];
    400 		return;
    401 	}
    402 	ungetc(c + 0x30, stdin);
    403 }
    404 
    405 static void
    406 input(String* s, ushort line, char* msg)
    407 {
    408 	uchar c;
    409 
    410 	for (;;) {
    411 		printf(CSI "%uH" EL "%s%s", line, msg, s->s);
    412 		switch (c = fgetc(stdin)) {
    413 			case Esc:
    414 				str_zero(s);
    415 			case '\n':
    416 				return;
    417 			case Del:
    418 				str_delc(s);
    419 				break;
    420 			default:
    421 				str_addc(s, c);
    422 		}
    423 	}
    424 }
    425 
    426 static int
    427 isword(uchar c)
    428 {
    429 	switch (c) {
    430 		case ' ': case '\t': case '\n': case '.':
    431 		case '(': case ')':  case '{':  case '}':
    432 		case '[': case ']':  case ':':  case ';':
    433 		case ',': case '<':  case '>':  case '#':
    434 		case '*': case '+':  case '-':  case '!':
    435 		case '%': case '\\': case '/':  case '"':
    436 		case '=':
    437 		return 0;
    438 	}
    439 	return 1;
    440 }
    441 
    442 static void
    443 insert(int arg)
    444 {
    445 	String s, c;
    446 
    447 	if (f->s->s[f->s->n - 1] != '\n') {
    448 		str_addc(f->s, '\n');
    449 		move(Right);
    450 	}
    451 	str_init(&s), str_init(&c);
    452 	str_addc(&c, '\0');
    453 	switch (arg) {
    454 		case StartLine:
    455 		case EndLine:
    456 		case Right:
    457 			move(arg);
    458 			break;
    459 		case Down:
    460 			move(EndLine);
    461 			str_addc(&s, '\n');
    462 			str_insert(f->s, &s, f->dot.p0);
    463 			++f->dot.p1;
    464 			break;
    465 	}
    466 	for (;;) {
    467 		resize();
    468 		fr_update();
    469 		switch (c.s[0] = fgetc(stdin)) {
    470 			case Esc:
    471 				goto endmode;
    472 			case Del:
    473 				if (f->dot.p1 != f->dot.p0) {
    474 					str_delc(&s);
    475 					str_delete(f->s, f->dot.p1 - 1, f->dot.p1);
    476 					--f->dot.p1;
    477 				}
    478 				break;
    479 			default:
    480 				str_addc(&s, c.s[0]);
    481 				str_insert(f->s, &c, f->dot.p1);
    482 		}
    483 		f->dot.p1 = f->dot.p0 + s.n;
    484 	}
    485 	endmode:
    486 	str_zero(&istr);
    487 	str_insert(&istr, &s, 0);
    488 	for (counter ? --counter : 0; counter; --counter) {
    489 		str_insert(&istr, &s, istr.n);
    490 		str_insert(f->s, &s, f->dot.p0);
    491 	}
    492 	buf_add(buf, &istr, NULL, Insert, arg, counter, f->dot.p0);
    493 	str_close(&s), str_close(&c);
    494 	f->dot.p0 = f->dot.p1;
    495 	if (f->dot.p1 >= f->s->n)
    496 		move(Left);
    497 }
    498 
    499 static void
    500 init()
    501 {
    502 	uint i;
    503 
    504 	win_init(&w);
    505 	for (i = 0; i < FILECOUNT; ++i) {
    506 		file_init(&file[i]);
    507 		fr_init(&frame[i]);
    508 	}
    509 	f = &file[0];
    510 	fr = &frame[0];
    511 	buf = &buffer[0];
    512 	str_init(&srch);
    513 	str_init(&istr);
    514 }
    515 
    516 static void
    517 file_close(int arg)
    518 {
    519 	if (arg == -1)
    520 		arg = f - file;
    521 	if (file[arg].dirty) {
    522 		printf(CSI "%uH" EL CSI "31mSave %s?" CSI "0m [y/n]", w.wy/2, file[arg].name->n ? file[arg].name->s : "-unnamed-");
    523 		if (fgetc(stdin) == 'y')
    524 			file_save(arg);
    525 	}
    526 	str_zero(f->s);
    527 	str_zero(f->name);
    528 	f->dot.p0 = f->dot.p1 = 0;
    529 }
    530 
    531 static void
    532 file_init(File* f)
    533 {
    534 	f->s = emalloc(sizeof(String));
    535 	f->name = emalloc(sizeof(String));
    536 	str_init(f->s);
    537 	str_init(f->name);
    538 	f->dirty = f->dot.p0 = f->dot.p1 = 0;
    539 	f->i = -1;
    540 }
    541 
    542 static void
    543 file_load(File* f)
    544 {
    545 	FILE* disk;
    546 
    547 	if (!(disk = fopen(f->name->s, "r")))
    548 		return;
    549 	fseek(disk, 0, SEEK_END);
    550 	if ((f->s->n = ftell(disk))) {
    551 		str_insure(f->s, f->s->n);
    552 		rewind(disk);
    553 		fread(f->s->s, f->s->n, 1, disk);
    554 	}
    555 	fclose(disk);
    556 	f->dot.p0 = f->dot.p1 = 0;
    557 	f->i = -1;
    558 	fr_zero(fr);
    559 }
    560 
    561 static void
    562 file_open(int c)
    563 {
    564 	if (f->dirty) {
    565 		printf(CSI "%uH" EL CSI "31mSave %s?" CSI "0m [y/n]", w.wy/2, f->name->s);
    566 		if (fgetc(stdin) == 'y')
    567 			file_save(f - file);
    568 	}
    569 	input(f->name, w.wy/2, CSI "32m$ " CSI "m");
    570 	file_load(f);
    571 }
    572 
    573 static void
    574 file_save(int arg)
    575 {
    576 	FILE* disk;
    577 
    578 	if (arg == -1)
    579 		arg = f - file;
    580 	if (!file[arg].name->n) {
    581 		input(file[arg].name, w.wy/2, "File name: ");
    582 		if (!file[arg].name->n)
    583 			return;
    584 	}
    585 	disk = fopen(file[arg].name->s, "w");
    586 	fwrite(file[arg].s->s, file[arg].s->n, 1, disk);
    587 	fclose(disk);
    588 	file[arg].dirty = 0;
    589 }
    590 
    591 static void
    592 fr_add(Frame* fr, Posn p0, Posn p1)
    593 {
    594 	fr_insure(fr, fr->n + 1);
    595 	fr->a[fr->n].p0 = p0;
    596 	fr->a[fr->n].p1 = p1;
    597 	++fr->n;
    598 }
    599 
    600 static void
    601 fr_calc(Frame* fr)
    602 {
    603 	Frame fr0;
    604 	Posn  p0, p1;
    605 	ushort half;
    606 
    607 	half = w.wy >> 1;
    608 	if (!fr->n
    609 		|| f->dot.p1 != f->dot.p0
    610 		|| f->dot.p1 < fr->a[0].p0
    611 		|| f->dot.p1 > fr->a[fr->n ? fr->n - 1 : 0].p1
    612 		|| (fr->dot < half && fr->a[0].p0)
    613 		|| (fr->dot + half + 1 > fr->n && fr->a[fr->n ? fr->n - 1 : 0].p1 + 1 < f->s->n)
    614 	) {
    615 		fr_zero(fr);
    616 		for (p0 = f->dot.p1; p0; --p0)
    617 			if (f->s->s[p0 - 1] == '\n')
    618 				break;
    619 		p1 = p0;
    620 		if (p1) {
    621 			fr_init(&fr0);
    622 			for (--p1; p1 && fr->n < w.wy;) {
    623 				for (;p1 && f->s->s[p1 - 1] != '\n'; --p1);
    624 				blind_reader(&fr0, f->s, p1);
    625 				p1 = fr0.a[0].p0;
    626 				if (p1)
    627 					--p1;
    628 				fr_insert(fr, &fr0, 0);
    629 				fr_zero(&fr0);
    630 			}
    631 			fr_close(&fr0);
    632 		}
    633 		for (p1 = p0; p1 < f->s->n && fr->n < w.wy * 3;) {
    634 			blind_reader(fr, f->s, p1);
    635 			p1 = fr->a[fr->n - 1].p1 + 1;
    636 		}
    637 	}
    638 	for (;f->dot.p1 < fr->a[fr->dot].p0 && fr->dot; --fr->dot);
    639 	for (;f->dot.p1 > fr->a[fr->dot].p1; ++fr->dot);
    640 }
    641 
    642 static void
    643 fr_close(Frame* fr)
    644 {
    645 	free(fr->a);
    646 }
    647 
    648 static void
    649 fr_init(Frame* fr)
    650 {
    651 	fr->a = emalloc(32 * sizeof(*fr->a));
    652 	fr->dot = 0;
    653 	fr->n = 0;
    654 	fr->size = 32;
    655 }
    656 
    657 static void
    658 fr_insert(Frame* p, Frame* q, ushort n)
    659 {
    660 	fr_insure(p, p->n + q->n);
    661 	memmove(p->a + n + q->n, p->a + n, (p->n - n) * sizeof(*p->a));
    662 	memmove(p->a + n, q->a, q->n * sizeof(*p->a));
    663 	p->n += q->n;
    664 }
    665 
    666 static void
    667 fr_insure(Frame* fr, ushort n)
    668 {
    669 	if (n > fr->size) {
    670 		fr->size += n + 32;
    671 		fr->a = erealloc(fr->a, fr->size * sizeof(*fr->a));
    672 	}
    673 }
    674 
    675 static void
    676 fr_update()
    677 {
    678 	Posn p0, p1;
    679 	uint half;
    680 
    681 	half = w.wy >> 1;
    682 	if (!f->s->n) {
    683 		fr->a[0].p0 = fr->a[0].p1 = 0;
    684 		printf(ED);
    685 		goto status;
    686 	}
    687 	fr_calc(fr);
    688 	fwrite(ED, sizeof(ED), 1, stdout);
    689 	blind_writer(f->s, fr, half, fr->dot, half, half + (w.wy % 2));
    690 	status:
    691 	printf(CSI "%uH", w.wy);
    692 	printf(STATUS);
    693 	for (p0 = fr->a[fr->dot].p0, p1 = 1; p0 < f->dot.p1; ++p0) {
    694 		if (f->s->s[p0] == '\t' && p1 % w.t)
    695 			p1 += (w.t + 1) - (p1 % w.t);
    696 		else
    697 			++p1;
    698 	}
    699 	printf(CSI "%u;%luH", half, p1);
    700 	return;
    701 }
    702 
    703 static void
    704 fr_zero(Frame* fr)
    705 {
    706 	fr->n = 0;
    707 	fr->dot = 0;
    708 }
    709 
    710 static void
    711 move(int arg)
    712 {
    713 	switch (arg) {
    714 		case Left:
    715 			if (f->dot.p1)
    716 				--f->dot.p1;
    717 			break;
    718 		case Right:
    719 			if (f->dot.p1 + 1 < f->s->n)
    720 				++f->dot.p1;
    721 			break;
    722 		case Up:
    723 			if (fr->dot)
    724 				f->dot.p1 = fr->a[fr->dot - 1].p0;
    725 			else
    726 				f->dot.p1 = 0;
    727 			break;
    728 		case Down:
    729 			if (!f->s->n)
    730 				return;
    731 			if (fr->dot < fr->n - 1)
    732 				f->dot.p1 = fr->a[fr->dot + 1].p0;
    733 			else
    734 				f->dot.p1 = fr->a[fr->dot].p1 - 1;
    735 			break;
    736 		case HalfUp:
    737 			if (fr->dot < w.wy/2)
    738 				f->dot.p1 = 0;
    739 			else
    740 				f->dot.p1 = fr->a[fr->dot - w.wy/2].p0;
    741 			break;
    742 		case HalfDown:
    743 			if (!f->s->n)
    744 				break;
    745 			if (fr->n - fr->dot <= w.wy/2) {
    746 				if (fr->a[fr->n - 1].p1 <= f->s->n)
    747 					f->dot.p1 = fr->a[fr->n - 1].p0;
    748 				else
    749 					f->dot.p1 = f->s->n;
    750 			} else
    751 				f->dot.p1 = fr->a[fr->dot + w.wy/2].p0;
    752 			break;
    753 		case Top:
    754 			f->dot.p1 = 0;
    755 			break;
    756 		case Bottom:
    757 			if (f->s->n)
    758 				f->dot.p1 = f->s->n - 1;
    759 			break;
    760 		case StartLine:
    761 			if (fr->dot)
    762 				for (;fr->dot > 1 && f->s->s[fr->a[fr->dot].p0 - 1] != '\n'; --fr->dot);
    763 			f->dot.p1 = fr->a[fr->dot].p0;
    764 			break;
    765 		case EndLine:
    766 			for (;f->dot.p1 + 1 < f->s->n && f->s->s[f->dot.p1] != '\n'; ++f->dot.p1);
    767 			break;
    768 		case Word:
    769 			for (;f->dot.p1 + 1 < f->s->n && isword(f->s->s[f->dot.p1]); ++f->dot.p1);
    770 			for (;f->dot.p1 + 1 < f->s->n && !isword(f->s->s[f->dot.p1]); ++f->dot.p1);
    771 			break;
    772 		case EndWord:
    773 			move(Right);
    774 			for (;f->dot.p1 < f->s->n && !isword(f->s->s[f->dot.p1]); ++f->dot.p1);
    775 			for (;f->dot.p1 < f->s->n && isword(f->s->s[f->dot.p1]); ++f->dot.p1);
    776 			move(Left);
    777 			break;
    778 		case PrevWord:
    779 			move(Left);
    780 			for (;f->dot.p1 > 0 && !isword(f->s->s[f->dot.p1]); --f->dot.p1);
    781 			for (;f->dot.p1 > 0 && isword(f->s->s[f->dot.p1]); --f->dot.p1);
    782 			if (f->dot.p1)
    783 				move(Right);
    784 			break;
    785 	};
    786 	f->dot.p0 = f->dot.p1;
    787 	fr_calc(fr);
    788 }
    789 
    790 static void
    791 quit(int arg)
    792 {
    793 	uint i;
    794 
    795 	for (i = 0; i < FILECOUNT; ++i)
    796 		file_close(i);
    797 	win_end();
    798 	exit(arg);
    799 }
    800 
    801 static void
    802 gmove(int arg)
    803 {
    804 	if (arg == Top) {
    805 		move(Top);
    806 		for (counter ? --counter : 0; counter; --counter) {
    807 			move(EndLine);
    808 			move(Right);
    809 		}
    810 		return;
    811 	}
    812 	for (!counter ? ++counter : 0; counter; --counter)
    813 		move(arg);
    814 }
    815 
    816 static void
    817 paste(int arg)
    818 {
    819 	str_insert(f->s, &istr, f->dot.p0);
    820 	buf_add(buf, &istr, NULL, Insert, 0, 1, f->dot.p0);
    821 }
    822 
    823 static void
    824 pline()
    825 {
    826 	ulong i, j, c, l;
    827 
    828 	for (l = f->dot.p1 = 0; f->dot.p1 < f->dot.p0; ++l) {
    829 		for (;f->dot.p1 + 1 < f->s->n && f->s->s[f->dot.p1] != '\n'; ++f->dot.p1);
    830 		if (f->dot.p1 + 1 < f->s->n)
    831 			++f->dot.p1;
    832 	}
    833 	++l;
    834 	j = 1;
    835 	if (!counter)
    836 		counter = 1;
    837 	for (;;) {
    838 		for (i = 0; i < j * counter && i < w.wy/2 ; ++i)
    839 			printf(CSI "%luH" EL "%lu", w.wy/2 + i, i ? i : l);
    840 		switch (c = fgetc(stdin)) {
    841 			case '!':
    842 				++j;
    843 				break;
    844 			case '+':
    845 				++counter;
    846 				break;
    847 			case '-':
    848 				if (counter > 1)
    849 					--counter;
    850 			default:
    851 				counter = 0;
    852 				return;
    853 		}
    854 	}
    855 }
    856 
    857 static void
    858 resize()
    859 {
    860 	Window wt;
    861 
    862 	win_query(&wt);
    863 	if (wt.wx != w.wx || wt.wy != w.wy) {
    864 		w.wx = wt.wx;
    865 		w.wy = wt.wy;
    866 		fr_zero(fr);
    867 	}
    868 }
    869 
    870 static void
    871 search(int arg)
    872 {
    873 	Posn pos;
    874 	char* p;
    875 
    876 	f->s->s[f->s->n] = 0;
    877 	if (arg == '/' || arg == '?') {
    878 		str_zero(&srch);
    879 		input(&srch, w.wy, "/");
    880 		for (pos = 0; pos < srch.n; ++pos)
    881 			if (srch.s[pos] == '^')
    882 				srch.s[pos] = '\n';
    883 	}
    884 	if (arg == '/' || arg == 'n') {
    885 		move(Right);
    886 		p = strstr(f->s->s + f->dot.p0, srch.s);
    887 		if (p == NULL) {
    888 			move(Left);
    889 			return;
    890 		}
    891 	} else {
    892 		pos = f->dot.p1;
    893 		if (srch.s[0] == '\n' && srch.s[1] != '\n')
    894 			move(Left);
    895 		for (;;) {
    896 			for (;move(Left), f->dot.p1 && f->s->s[f->dot.p1] != srch.s[0];);
    897 			if (!strncmp(f->s->s + f->dot.p1, srch.s, srch.n))
    898 				break;
    899 			if (!f->dot.p1) {
    900 				f->dot.p0 = f->dot.p1 = pos;
    901 				return;
    902 			}
    903 		}
    904 		p = f->s->s + f->dot.p1;
    905 	}
    906 	f->dot.p0 = f->dot.p1 = p - f->s->s;
    907 	if (srch.s[0] == '\n' && srch.s[1] != '\n')
    908 		move(Right);
    909 	fr_update();
    910 }
    911 
    912 static int
    913 selection(int arg)
    914 {
    915 	Posn p0;
    916 
    917 	if (!counter)
    918 		++counter;
    919 	if (arg > 0x7f) {
    920 		arg -= 0x7f;
    921 		goto till;
    922 	}
    923 	p0 = f->dot.p1 = f->dot.p0;
    924 	switch (arg) {
    925 		case Letter:
    926 			p0 = f->dot.p0;
    927 			for (;counter > 1; --counter)
    928 				move(Right);
    929 			break;
    930 		case Line:
    931 			move(StartLine);
    932 			p0 = f->dot.p0;
    933 			for (;counter; --counter) {
    934 				move(EndLine);
    935 				move(Right);
    936 			}
    937 			if (f->dot.p1 + 1 < f->s->n)
    938 				move(Left);
    939 			break;
    940 		case Bottom:
    941 			move(StartLine);
    942 			move(Left);
    943 			p0 = f->dot.p0;
    944 			move(Bottom);
    945 			break;
    946 		case Top:
    947 			p0 = 0;
    948 			move(EndLine);
    949 			break;
    950 		case Word:
    951 			p0 = f->dot.p0;
    952 			for (;counter; --counter)
    953 				move(EndWord);
    954 			break;
    955 		case Till:
    956 			arg = fgetc(stdin);
    957 			if (arg == Esc)
    958 				return 0;
    959 			till:
    960 			p0 = f->dot.p0;
    961 			for (;counter && f->dot.p1 + 1 < f->s->n; --counter)
    962 				for (++f->dot.p1; f->dot.p1 + 1 < f->s->n && f->s->s[f->dot.p1 + 1] != arg; ++f->dot.p1);
    963 			if (f->s->s[f->dot.p1 + 1] != arg) {
    964 				f->dot.p1 = f->dot.p0;
    965 				return -1;
    966 			}
    967 			arg += 0x7f;
    968 			break;
    969 	}
    970 	f->dot.p0 = p0;
    971 	counter = 0;
    972 	return arg;
    973 }
    974 
    975 static void
    976 str_init(String* p)
    977 {
    978 	p->s = emalloc(MINSIZE * sizeof(*p->s));
    979 	p->n = 0;
    980 	p->size = MINSIZE;
    981 }
    982 
    983 static void
    984 str_close(String* p)
    985 {
    986 	free(p->s);
    987 }
    988 
    989 static void
    990 str_zero(String* p)
    991 {
    992 	if (p->size > MAXEMPTY) {
    993 		p->s = erealloc(p->s, MAXEMPTY * sizeof(*p->s));
    994 		p->size = MAXEMPTY;
    995 	}
    996 	p->n = 0;
    997 	memset(p->s, 0, p->size);
    998 }
    999 
   1000 static void
   1001 str_insure(String* p, ulong n)
   1002 {
   1003 	if (p->size < n) {
   1004 		p->size = n + MAXEMPTY;
   1005 		p->s = erealloc(p->s, p->size * sizeof(*p->s));
   1006 	}
   1007 }
   1008 
   1009 static void
   1010 str_addc(String* p, int c)
   1011 {
   1012 	str_insure(p, p->n + 1);
   1013 	p->s[p->n++] = c;
   1014 	p->s[p->n] = '\0';
   1015 }
   1016 
   1017 static void
   1018 str_adds(String* p, char* s, ulong n)
   1019 {
   1020 	str_insure(p, p->n + n);
   1021 	memmove(p->s + p->n, s, n);
   1022 	p->n += n;
   1023 }
   1024 
   1025 static void
   1026 str_delc(String* p)
   1027 {
   1028 	if (p->n)
   1029 		p->s[--p->n] = 0;
   1030 }
   1031 
   1032 static void
   1033 str_insert(String* p, String* q, Posn p0)
   1034 {
   1035 	str_insure(p, p->n + q->n);
   1036 	memmove(p->s + p0 + q->n, p->s + p0, p->n - p0);
   1037 	memmove(p->s + p0, q->s, q->n);
   1038 	p->n += q->n;
   1039 }
   1040 
   1041 static void
   1042 str_delete(String* p, Posn p0, Posn p1)
   1043 {
   1044 	memmove(p->s + p0, p->s + p1, p->n - p1);
   1045 	p->n -= p1 - p0;
   1046 }
   1047 
   1048 static void
   1049 undo(int arg)
   1050 {
   1051 	if (f->i < 0)
   1052 		return;
   1053 	if (buf->is[f->i].n)
   1054 		str_delete(f->s, buf->p0[f->i], buf->p0[f->i] + buf->is[f->i].n);
   1055 	if (buf->ds[f->i].n)
   1056 		str_insert(f->s, &buf->ds[f->i], buf->p0[f->i]);
   1057 	f->dot.p0 = f->dot.p1 = buf->p0[f->i--];
   1058 	f->dirty = '*';
   1059 	fr_zero(fr);
   1060 	fr_update();
   1061 }
   1062 
   1063 static void
   1064 yank(int arg)
   1065 {
   1066 	if (!f->s->n)
   1067 		return;
   1068 	if (!arg) {
   1069 		switch (arg = fgetc(stdin)) {
   1070 			case 'y': arg = Line; break;
   1071 			case 'G': arg = Bottom; break;
   1072 			case 'g': arg = Top; break;
   1073 			case 'w': arg = Word; break;
   1074 			case 't': arg = Till; break;
   1075 			default:
   1076 				return;
   1077 		}
   1078 	}
   1079 	if ((arg = selection(arg)) < 0)
   1080 		return;
   1081 	str_zero(&istr);
   1082 	str_adds(&istr, f->s->s + f->dot.p0, f->dot.p1 + 1 - f->dot.p0);
   1083 }
   1084 
   1085 int
   1086 main(int argc, char* argv[])
   1087 {
   1088 	uint i;
   1089 	uchar c;
   1090 
   1091 	init();
   1092 	if (argv[1]) {
   1093 		str_adds(f->name, argv[1], strlen(argv[1]));
   1094 		file_load(f);
   1095 	}
   1096 	for (;;) {
   1097 		resize();
   1098 		fr_update();
   1099 		c = fgetc(stdin);
   1100 		for (i = 0; i < LENGTH(keys); ++i) {
   1101 			if (keys[i].key == c) {
   1102 				keys[i].func(keys[i].value);
   1103 				break;
   1104 			}
   1105 		}
   1106 	}
   1107 }