Subversion Repositories Krakow BASIC

Rev

Rev 3 | Rev 6 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 nishi 1
/* $Id: basic.c 4 2024-09-03 22:40:08Z nishi $ */
2
 
3
/* Krakow BASIC - Multi-platform simple BASIC */
4
 
5
#if defined(PLATFORM_SHIROI)
6
 
7
#include "dri/text.h"
8
#include "dri/video.h"
9
#include "dri/math.h"
10
 
11
#include "mem.h"
12
#include "char.h"
13
#define PLATFORM "Shiroi"
14
#define NEWLINE "\r\n"
15
#define BREAKKEY
16
 
4 nishi 17
#elif defined(PLATFORM_UNIX) || defined(PLATFORM_WINDOWS) || defined(PLATFORM_ARDUINO) || defined(PLATFORM_C64)
2 nishi 18
 
19
#if defined(PLATFORM_WINDOWS)
20
#define PLATFORM "Windows"
21
#elif defined(PLATFORM_UNIX)
22
#define PLATFORM "Unix"
23
#elif defined(PLATFORM_ARDUINO)
24
#define PLATFORM "Arduino"
25
#define NEWLINE "\r\n"
26
#define BREAKKEY
4 nishi 27
#elif defined(PLATFORM_C64)
28
#define PLATFORM "Commodore-64"
29
#define NEWLINE "\r\n"
30
#define BREAKKEY
31
#include <conio.h>
2 nishi 32
#endif
33
 
4 nishi 34
#define mull(x, y) ((x) * (y))
35
#define divl(x, y) ((x) / (y))
36
#define killcursor(x)
37
#define cursor(x)
38
#define strnum atoi
39
 
2 nishi 40
#ifndef NEWLINE
41
#define NEWLINE "\n"
42
#endif
43
 
44
#include <stdio.h>
45
#include <stdlib.h>
46
#include <string.h>
47
#include <ctype.h>
48
#include <stdbool.h>
49
 
50
#if defined(__MINGW32__)
51
#include <conio.h>
52
#include <windows.h>
53
#elif defined(PLATFORM_ARDUINO)
54
#define BAUD 9600
55
#include <avr/io.h>
56
#include <util/delay.h>
57
#include <util/setbaud.h>
58
#define BUFFER_SIZE (1024)
59
#define LINE_BUFFER_SIZE (128)
60
#define LINES (32)
61
#undef putchar
62
#define putchar uart_putchar
63
#elif defined(PLATFORM_UNIX)
64
#include <termios.h>
4 nishi 65
#elif defined(PLATFORM_C64)
66
#define BUFFER_SIZE (16 * 1024)
67
#undef killcursor
68
#undef cursor
69
#define killcursor(x) cursor(0)
70
#define cursor(x) cursor(1)
2 nishi 71
#endif
72
 
73
#if defined(PLATFORM_ARDUINO)
74
int uart_putchar(char c) {
75
	while(!(UCSR0A & _BV(UDRE0)))
76
		;
77
	UDR0 = c;
78
	return 0;
79
}
80
 
81
void uart_init(void) {
82
	UBRR0H = UBRRH_VALUE;
83
	UBRR0L = UBRRL_VALUE;
84
 
85
	UCSR0B |= _BV(TXEN0) | _BV(RXEN0);
86
 
87
	UCSR0C |= _BV(UCSZ00) | _BV(UCSZ01);
88
}
89
#endif
90
 
4 nishi 91
#define agetch(x) oggetch(0)
92
char oggetch(char wait) {
93
	int c;
2 nishi 94
#if defined(PLATFORM_WINDOWS)
95
rescan:
96
	c = _getch();
97
	if(c == '\r') return '\n';
98
	if(c == '\n') goto rescan;
99
#elif defined(PLATFORM_UNIX)
4 nishi 100
	c = getchar();
2 nishi 101
	if(c == EOF) return -1;
102
	if(c == '\r') return '\n';
103
#elif defined(PLATFORM_ARDUINO)
104
rescan:
4 nishi 105
	if(wait){
106
		if(!(UCSR0A & _BV(RXC0))) return 0;
107
	}else{
108
		while(!(UCSR0A & _BV(RXC0)));
109
	}
2 nishi 110
	c = UDR0;
111
	if(c == '\r') return '\n';
112
	if(c == '\n') goto rescan;
113
	if(c == 3) return 1;
4 nishi 114
#elif defined(PLATFORM_C64)
115
	if(!wait){
116
		if(!kbhit()) return 0;
117
	}
118
	c = cgetc();
119
	if(c == EOF) return -1;
120
	if(c == '\r') return '\n';
121
	if(c == 20) return 8;
122
	if(c == 3) return 1;
2 nishi 123
#endif
124
	return c;
125
}
126
 
127
bool strcaseequ(const char* a, const char* b) { return strcasecmp(a, b) == 0; }
128
 
129
#if defined(PLATFORM_ARDUINO)
130
void putstr(const char* n) {
131
	int i;
132
	for(i = 0; n[i] != 0; i++) {
133
		uart_putchar(n[i]);
134
	}
135
}
136
 
137
void putnum(int n) {
138
	char buf[64];
139
	int incr = 63;
140
	buf[incr--] = 0;
141
	while(1) {
142
		buf[incr--] = (n % 10) + '0';
143
		n /= 10;
144
		if(n == 0) break;
145
	}
146
	putstr(buf + incr + 1);
147
}
148
#else
149
void putnum(int n) {
150
	printf("%d", n);
151
	fflush(stdout);
152
}
153
 
154
void putstr(const char* n) {
155
	printf("%s", n);
156
	fflush(stdout);
157
}
158
#endif
159
 
160
void change_color(int a) {
4 nishi 161
#if defined(PLATFORM_ARDUINO) || defined(PLATFORM_UNIX)
2 nishi 162
	int fg = (a >> 4) & 0xf;
163
	int bg = (a & 0xf);
4 nishi 164
	char color[2];
2 nishi 165
	if(!(0 <= fg && fg <= 15)) return;
166
	if(!(0 <= bg && bg <= 15)) return;
167
	color[1] = 0;
3 nishi 168
	if(bg < 8) {
2 nishi 169
		color[0] = bg + '0';
170
		putstr("\x1b[4");
3 nishi 171
	} else {
2 nishi 172
		color[0] = (bg - 8) + '0';
173
		putstr("\x1b[10");
174
	}
175
	putstr(color);
176
	putstr("m");
3 nishi 177
	if(fg < 8) {
2 nishi 178
		color[0] = fg + '0';
179
		putstr("\x1b[3");
3 nishi 180
	} else {
2 nishi 181
		color[0] = (fg - 8) + '0';
182
		putstr("\x1b[9");
183
	}
184
	putstr(color);
185
	putstr("m");
186
	putstr("\x1b[2J\x1b[1;1H");
4 nishi 187
#elif defined(PLATFORM_C64)
188
	int fg = (a >> 4) & 0xf;
189
	int bg = (a & 0xf);
190
	bgcolor(bg);
191
	textcolor(fg);
192
#endif
2 nishi 193
}
194
 
195
void clear(void) {
196
#if defined(PLATFORM_WINDOWS)
197
	system("cls");
198
#elif defined(PLATFORM_UNIX) || defined(PLATFORM_ARDUINO)
199
	putstr("\x1b[0m\x1b[2J\x1b[1;1H");
4 nishi 200
#elif defined(PLATFORM_C64)
201
	clrscr();
2 nishi 202
#endif
203
}
204
 
205
void basic(void);
206
 
207
int main() {
208
#if defined(PLATFORM_WINDOWS)
209
	HANDLE winstdout = GetStdHandle(STD_OUTPUT_HANDLE);
210
	DWORD mode = 0;
211
	GetConsoleMode(winstdout, &mode);
212
	const DWORD origmode = mode;
213
	mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
214
	SetConsoleMode(winstdout, mode);
215
#elif defined(PLATFORM_UNIX)
216
	struct termios old, new;
217
	tcgetattr(0, &old);
218
	new = old;
219
	new.c_lflag &= ~(ECHO | ICANON);
220
	tcsetattr(0, TCSANOW, &new);
221
#elif defined(PLATFORM_ARDUINO)
222
	uart_init();
223
	DDRB |= _BV(DDB5);
224
	PORTB |= _BV(PORT5);
225
#endif
4 nishi 226
#if defined(PLATFORM_C64)
227
	change_color((1 << 4) | 0);
228
#endif
2 nishi 229
	basic();
230
#if defined(PLATFORM_WINDOWS)
231
	SetConsoleMode(winstdout, origmode);
232
#elif defined(PLATFORM_UNIX)
233
	tcsetattr(0, TCSANOW, &old);
234
#endif
235
}
236
 
237
#else
238
#error "Define PLATFORM_*"
239
#endif
240
 
241
#define VERSION "0.0"
242
 
243
#ifndef LINE_BUFFER_SIZE
244
#define LINE_BUFFER_SIZE (512)
245
#endif
246
 
247
#ifndef BUFFER_SIZE
248
#define BUFFER_SIZE (1024 * 24)
249
#endif
250
 
251
#ifndef LINES
252
#define LINES (1024)
253
#endif
254
 
255
unsigned char basicbuffer[BUFFER_SIZE];
256
char linebuf[LINE_BUFFER_SIZE];
257
 
258
int pexpr(char* expr, char* buffer, int* number) {
259
	char ownbuf[128];
260
	int i;
261
	int start = 0;
262
	int br = 0;
263
	int result = 0;
264
	int stack[32];
265
	int sp = 0;
266
	char put = 0;
4 nishi 267
	for(i = 0; expr[i] != 0; i++) ownbuf[i] = expr[i];
268
	ownbuf[i] = 0;
2 nishi 269
	for(i = 0; i < 32; i++) stack[i] = 0;
270
	for(i = 0;; i++) {
271
		if(ownbuf[i] == 0) {
272
			break;
273
		} else if('0' <= ownbuf[i] && ownbuf[i] <= '9') {
274
			stack[sp] *= 10;
275
			stack[sp] += ownbuf[i] - '0';
276
			put = 1;
277
		} else if(ownbuf[i] == '+' || ownbuf[i] == '-' || ownbuf[i] == '*' || ownbuf[i] == '/') {
278
			put = 0;
279
			if(sp < 2) {
280
				return -1;
281
			} else {
282
				int top = stack[--sp];
283
				int bottom = stack[--sp];
284
				int value = 0;
285
				if(ownbuf[i] == '+') {
286
					value = top + bottom;
287
				} else if(ownbuf[i] == '-') {
288
					value = bottom + top;
289
				} else if(ownbuf[i] == '*') {
290
					value = mull(top, bottom);
291
				} else if(ownbuf[i] == '/') {
292
					value = divl(bottom, top);
293
				}
294
				stack[sp++] = value;
295
			}
296
			stack[sp] = 0;
297
		} else if(ownbuf[i] == ' ' && put == 1) {
298
			stack[++sp] = 0;
299
		}
300
	}
301
	result = stack[0];
302
	*number = result;
303
	return 1;
304
}
305
 
306
/* arr gets sorted, arr2 index gets replaced */
307
void sort(int* arr, int* arr2, int size) {
308
	int i;
309
redo:
310
	for(i = 1; i < size; i++) {
311
		if(arr[i - 1] > arr[i]) {
312
			int tmp = arr[i];
313
			arr[i] = arr[i - 1];
314
			arr[i - 1] = tmp;
315
			tmp = arr2[i];
316
			arr2[i] = arr2[i - 1];
317
			arr2[i - 1] = tmp;
318
		}
319
	}
320
	for(i = 1; i < size; i++) {
321
		if(arr[i - 1] > arr[i]) {
322
			goto redo;
323
		}
324
	}
325
}
326
 
327
int run(char* cmd, int linenum, char num, int* lgoto) {
4 nishi 328
	char line[LINE_BUFFER_SIZE];
329
	char rcmd[32];
330
	int i;
331
	char* arg;
332
	int incr = 0;
2 nishi 333
#ifdef BREAKKEY
334
	if(agetch() == 1) return -1;
335
#endif
336
	if(lgoto != 0) *lgoto = 0;
337
	for(i = 0; cmd[i] != 0; i++) line[i] = cmd[i];
338
	line[i] = 0;
339
	rcmd[0] = 0;
340
	for(i = 0;; i++) {
341
		if(line[i] == ' ' || line[i] == '\t' || line[i] == 0 || line[i] == '"') {
342
			break;
343
		} else {
344
			rcmd[incr++] = line[i];
345
			if(incr == 32) {
346
				putstr("! Command too long");
347
				if(linenum != -1) {
348
					putstr(" in line ");
349
					putnum(linenum);
350
				}
351
				putstr(NEWLINE);
352
				return 1;
353
			}
354
			rcmd[incr] = 0;
355
		}
356
	}
4 nishi 357
	arg = line + 1 + strlen(rcmd);
2 nishi 358
	if(strcaseequ(rcmd, "COLOR")) {
359
		int argc = 0;
360
		char* farg = 0;
361
		char* sarg = 0;
4 nishi 362
		int fgcolor, bgcolor, ret0, ret1;
2 nishi 363
		if(arg[0] != 0) argc++;
364
		for(i = 0; arg[i] != 0; i++) {
365
			if(arg[i] == ',') {
366
				arg[i] = 0;
367
				farg = arg;
368
				sarg = arg + i + 1;
369
				for(; *sarg != 0 && (*sarg == '\t' || *sarg == ' '); sarg++)
370
					;
371
				argc++;
372
			}
373
		}
374
		if(argc != 2) {
375
			putstr("! Invalid argument");
376
			if(linenum != -1) {
377
				putstr(" in line ");
378
				putnum(linenum);
379
			}
380
			putstr(NEWLINE);
381
			return 1;
382
		}
4 nishi 383
		bgcolor = 0;
384
		fgcolor = 0;
385
		ret0 = pexpr(farg, 0, &bgcolor);
386
		ret1 = pexpr(sarg, 0, &fgcolor);
2 nishi 387
		if(ret0 == 0) {
388
			putstr("! Invalid argument");
389
			if(linenum != -1) {
390
				putstr(" in line ");
391
				putnum(linenum);
392
			}
393
			putstr(NEWLINE);
394
			return 1;
395
		} else if(ret0 == -1) {
396
			putstr("! Syntax error");
397
			if(linenum != -1) {
398
				putstr(" in line ");
399
				putnum(linenum);
400
			}
401
			putstr(NEWLINE);
402
			return 1;
403
		}
404
		if(ret1 == 1) {
405
			change_color((fgcolor << 4) | bgcolor);
406
		} else if(ret1 == 0) {
407
			putstr("! Invalid argument");
408
			if(linenum != -1) {
409
				putstr(" in line ");
410
				putnum(linenum);
411
			}
412
			putstr(NEWLINE);
413
			return 1;
414
		} else if(ret1 == -1) {
415
			putstr("! Syntax error");
416
			if(linenum != -1) {
417
				putstr(" in line ");
418
				putnum(linenum);
419
			}
420
			putstr(NEWLINE);
421
			return 1;
422
		}
423
	} else if(num == 0 && strcaseequ(rcmd, "LIST")) {
424
		int addr = BUFFER_SIZE - 1;
425
		int saddr = 0;
426
		int lbuf[LINES];
427
		int shift[LINES];
428
		int cnt = 0;
4 nishi 429
		int i;
2 nishi 430
		while(1) {
431
			unsigned long ln = 0;
432
			for(i = 0; i < 4; i++) {
433
				ln >>= 8;
434
				ln |= (unsigned long)basicbuffer[addr--] << 24;
435
			}
436
			if(ln == 0) break;
437
			lbuf[cnt] = ln;
438
			shift[cnt++] = saddr;
439
			saddr += strlen(basicbuffer + saddr) + 1;
440
		}
441
		sort(lbuf, shift, cnt);
442
		for(i = 0; i < cnt; i++) {
443
			putnum(lbuf[i]);
444
			putstr(" ");
445
			putstr(basicbuffer + shift[i]);
446
			putstr(NEWLINE);
447
		}
448
	} else if(num == 1 && strcaseequ(rcmd, "GOTO")) {
449
		int ret = pexpr(arg, 0, lgoto);
450
		if(ret == 0) {
451
			putstr("! Invalid argument");
452
			if(linenum != -1) {
453
				putstr(" in line ");
454
				putnum(linenum);
455
			}
456
			putstr(NEWLINE);
457
			return 1;
458
		} else if(ret == -1) {
459
			putstr("! Syntax error");
460
			if(linenum != -1) {
461
				putstr(" in line ");
462
				putnum(linenum);
463
			}
464
			putstr(NEWLINE);
465
			return 1;
466
		}
467
	} else if(num == 0 && strcaseequ(rcmd, "RUN")) {
468
		int addr = BUFFER_SIZE - 1;
469
		int saddr = 0;
470
		int lbuf[LINES];
471
		int shift[LINES];
472
		int cnt = 0;
4 nishi 473
		int gt, i;
2 nishi 474
		while(1) {
475
			unsigned long ln = 0;
476
			for(i = 0; i < 4; i++) {
477
				ln >>= 8;
478
				ln |= (unsigned long)basicbuffer[addr--] << 24;
479
			}
480
			if(ln == 0) break;
481
			lbuf[cnt] = ln;
482
			shift[cnt++] = saddr;
483
			saddr += strlen(basicbuffer + saddr) + 1;
484
		}
485
		sort(lbuf, shift, cnt);
486
		for(i = 0; i < cnt; i++) {
487
			int ret = run(basicbuffer + shift[i], lbuf[i], 1, &gt);
488
			if(ret != 0) return ret;
489
			if(gt != 0) {
4 nishi 490
				char found;
2 nishi 491
				for(i = 0; i < cnt; i++) {
492
					if(lbuf[i] == gt) {
493
						found = 1;
494
						break;
495
					}
496
				}
497
				if(found) {
498
					i--;
499
				} else {
500
					putstr("! GOTO no such line");
501
					if(linenum != -1) {
502
						putstr(" in line ");
503
						putnum(linenum);
504
					}
505
					putstr(NEWLINE);
506
					return 1;
507
				}
508
			}
509
		}
510
	} else {
511
		putstr("! Unknown command");
512
		if(linenum != -1) {
513
			putstr(" in line ");
514
			putnum(linenum);
515
		}
516
		putstr(NEWLINE);
517
		return 1;
518
	}
519
	return 0;
520
}
521
 
522
int execute(int linenum, char* cmd, char num) {
523
	int i;
524
	char line[LINE_BUFFER_SIZE];
525
	int incr = 0;
526
	char dq = 0;
527
	for(i = 0; cmd[i] != 0; i++) {
528
		if(cmd[i] == ' ' || cmd[i] == '\t') {
529
			if(!dq) {
530
				for(; cmd[i] != 0 && (cmd[i] == ' ' || cmd[i] == '\t'); i++)
531
					;
532
				i--;
533
			}
534
			line[incr++] = cmd[i];
535
		} else if(cmd[i] == '"') {
536
			line[incr++] = '"';
537
			dq = dq == 0 ? 1 : 0;
538
		} else {
539
			line[incr++] = dq == 0 ? toupper(cmd[i]) : cmd[i];
540
		}
541
	}
542
	line[incr] = 0;
543
	if(num == 0) {
544
		int ret = run(line, -1, 0, 0);
545
		putstr("Ready\r\n");
546
		return ret;
547
	} else {
548
		int addr = BUFFER_SIZE - 1;
549
		int i;
550
		int shf = 0;
551
		int cnt = 0;
4 nishi 552
		int len;
2 nishi 553
		while(1) {
554
			unsigned long ln = 0;
555
			for(i = 0; i < 4; i++) {
556
				ln >>= 8;
557
				ln |= (unsigned long)basicbuffer[addr--] << 24;
558
			}
559
			cnt++;
560
			if(ln == linenum) shf = cnt;
561
			if(shf != 0) {
562
				addr += 4;
563
				for(i = 0; i < 4; i++) {
564
					basicbuffer[addr] = basicbuffer[addr - 4 * shf];
565
					addr--;
566
				}
567
				addr += 4;
568
				ln = 0;
569
				for(i = 0; i < 4; i++) {
570
					ln >>= 8;
571
					ln |= (unsigned long)basicbuffer[addr--] << 24;
572
				}
573
			}
574
			if(ln == 0) break;
575
		}
576
		if(line[0] != 0) {
577
			addr += 4;
578
			for(i = 0; i < 4; i++) {
579
				basicbuffer[addr--] = linenum & 0xff;
580
				linenum >>= 8;
581
			}
582
		}
4 nishi 583
		len = 0;
2 nishi 584
		cnt = 0;
585
		while(1) {
586
			int slen = strlen(basicbuffer + len);
587
			if(slen == 0) break;
588
			len += slen + 1;
589
			cnt++;
590
		}
591
		if(line[0] != 0) {
592
			for(i = 0; line[i] != 0; i++) {
593
				basicbuffer[len + i] = line[i];
594
			}
595
			basicbuffer[len + i] = 0;
596
			basicbuffer[len + i + 1] = 0;
597
		}
598
		if(shf != 0) {
599
			cnt = 0;
600
			len = 0;
601
			while(1) {
602
				int slen = strlen(basicbuffer + len);
603
				if(slen == 0) break;
604
 
605
				len += slen + 1;
606
				cnt++;
607
 
608
				if(cnt == shf) {
609
					int i;
610
					int nc = 0;
4 nishi 611
					len -= slen + 1;
2 nishi 612
					for(i = len;; i++) {
613
						basicbuffer[i] = basicbuffer[i + slen + 1];
614
						if(basicbuffer[i] == 0) {
615
							nc++;
616
							if(nc == 2) break;
617
						} else {
618
							nc = 0;
619
						}
620
					}
621
					break;
622
				}
623
			}
624
		}
625
		return 0;
626
	}
627
}
628
 
629
void basic(void) {
630
	int i;
4 nishi 631
	int lineind;
2 nishi 632
	clear();
633
 
634
#ifdef SMALL
635
	putstr("Krakow BASIC  Ver. ");
636
	putstr(VERSION);
637
	putstr(NEWLINE);
638
	putstr(NEWLINE);
639
#else
640
	putstr(PLATFORM);
641
	putstr("   Krakow BASIC V");
642
	putstr(VERSION);
643
	putstr("\r\n");
644
	putstr("Copyright 2024 by: Nishi.\r\n");
645
	putstr("                   penguin2233.");
646
	putstr(NEWLINE);
647
	putstr(NEWLINE);
648
	putstr(" Max ");
649
	putnum(LINE_BUFFER_SIZE);
650
	putstr(" characters per line");
651
	putstr(NEWLINE);
652
	putstr(" Max ");
653
	putnum(LINES);
654
	putstr(" lines");
655
	putstr(NEWLINE);
656
	putstr(" ");
657
#endif
658
	putnum(BUFFER_SIZE);
659
	putstr(" bytes free");
660
	putstr(NEWLINE);
661
	putstr(NEWLINE);
662
 
663
	for(i = 0; i < BUFFER_SIZE; i++) {
664
		basicbuffer[i] = 0;
665
	}
666
	putstr("Ready");
667
	putstr(NEWLINE);
668
 
669
	cursor();
670
	linebuf[0] = 0;
4 nishi 671
	lineind = 0;
2 nishi 672
	while(1) {
4 nishi 673
		char c;
674
#if defined(PLATFORM_C64)
675
		c = oggetch(1);
676
#else
677
		c = agetch();
678
#endif
2 nishi 679
		if(c != 0) killcursor();
680
		if(c == 1) {
681
			putstr("Break");
682
			putstr(NEWLINE);
683
			lineind = 0;
684
		} else if(c == '\n') {
4 nishi 685
			int i;
686
			char num = 1;
687
			char* cmd = linebuf;
688
 
2 nishi 689
			linebuf[lineind] = 0;
690
			putstr(NEWLINE);
691
			if(lineind == 0) goto skip;
692
 
693
			for(i = 0; linebuf[i] != 0; i++) {
694
				if(linebuf[i] == ' ') {
695
					linebuf[i] = 0;
696
					cmd = linebuf + i + 1;
697
					break;
698
				} else if(!('0' <= linebuf[i] && linebuf[i] <= '9')) {
699
					num = 0;
700
					break;
701
				}
702
			}
703
 
704
			if(num == 1 && strnum(linebuf) == 0) {
705
				putstr("! Line number 0 is illegal");
706
				putstr(NEWLINE);
707
			} else {
708
				int ret = execute(num == 1 ? strnum(linebuf) : -1, (num == 1 && cmd == linebuf) ? "" : cmd, num);
709
				if(ret == -1) {
710
					putstr("! Break");
711
					putstr(NEWLINE);
712
				}
713
			}
714
 
715
		skip:
716
			lineind = 0;
717
		} else if(c == 0x8 || c == 0x7f) {
718
			if(lineind > 0) {
719
				putstr("\x08 \x08");
720
				linebuf[--lineind] = 0;
721
			}
722
		} else if(c == -1) {
723
			break;
724
		} else if(c != 0) {
725
			putchar(c);
726
			linebuf[lineind++] = c;
727
		}
728
		cursor();
729
	}
730
}