sim

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

sim.c (20534B)


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