sim

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

sim.c (20747B)


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