sim

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

sim.c (20448B)


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