|
|
|
@@ -0,0 +1,818 @@ |
|
|
|
/* vim128.c - a small modal editor for the Commodore 128, built with cc65. |
|
|
|
* |
|
|
|
* cl65 -t c128 -O -o vim128.prg vim128.c |
|
|
|
* |
|
|
|
* A port of the Power C / C64 editor (vim.c, vscreen.c, vview.c, vedit.c). |
|
|
|
* The Power C version needed hand-written machine code and direct screen |
|
|
|
* pokes; here cc65's conio draws the screen, so the same program works on the |
|
|
|
* 40 column VIC screen and on the 80 column VDC screen (it uses whatever |
|
|
|
* screensize() reports). |
|
|
|
* |
|
|
|
* move h j k l / arrows, 0 $ gg G |
|
|
|
* insert i a A I o O (RUN/STOP or ESC leaves insert mode, DEL backspaces) |
|
|
|
* change x dd p |
|
|
|
* colon :w :w name :wq :x :q :q! |
|
|
|
* |
|
|
|
* The buffer holds the file exactly as it sits on disk: PETSCII bytes with $0d |
|
|
|
* between lines. Keys are PETSCII too: unshifted letters arrive as $41-$5a and |
|
|
|
* shifted ones as $c1-$da, which is what KL() and KU() below spell. |
|
|
|
*/ |
|
|
|
|
|
|
|
#include <conio.h> |
|
|
|
#include <cbm.h> |
|
|
|
#include <c128.h> |
|
|
|
#include <string.h> |
|
|
|
|
|
|
|
#define BUFMAX 19000u /* bytes of file text */ |
|
|
|
#define MAXLN 600 /* lines */ |
|
|
|
#define MAXCOL 80 /* longest line kept */ |
|
|
|
#define NAMEMAX 20 |
|
|
|
|
|
|
|
/* cc65 translates character literals to PETSCII for the c128 target, so 'h' |
|
|
|
is already $48 (what the unshifted key sends) and 'G' is $c7 (shifted). */ |
|
|
|
#define KL(c) (c) /* unshifted letter, e.g. KL('h') */ |
|
|
|
#define KU(c) (c) /* shifted letter, e.g. KU('G') */ |
|
|
|
|
|
|
|
#define K_STOP 3 |
|
|
|
#define K_ESC 27 |
|
|
|
#define K_DEL 20 |
|
|
|
#define K_RET 13 |
|
|
|
#define K_DOWN 17 |
|
|
|
#define K_UP 145 |
|
|
|
#define K_RIGHT 29 |
|
|
|
#define K_LEFT 157 |
|
|
|
|
|
|
|
static unsigned char bfr[BUFMAX]; /* file text, $0d separated */ |
|
|
|
static unsigned blen; /* bytes used */ |
|
|
|
static unsigned lptr[MAXLN]; /* offset of each line start */ |
|
|
|
static int nlines; |
|
|
|
static int cy, cx; /* cursor line and column */ |
|
|
|
static int top, leftcol; /* first line / column on screen */ |
|
|
|
static char fname[NAMEMAX + 1]; |
|
|
|
static int mode; /* 0 normal, 1 insert */ |
|
|
|
static int dirty; /* changed since the last save */ |
|
|
|
static int pend; /* first key of dd / gg */ |
|
|
|
static const char *note; /* one-shot status message */ |
|
|
|
static unsigned char ybuf[MAXCOL]; /* the yanked line */ |
|
|
|
static int ylen, yok; |
|
|
|
static int redraw; /* 1 cursor row, 2 whole screen */ |
|
|
|
static int split; /* a long line was split on load */ |
|
|
|
static unsigned char dev; /* disk device */ |
|
|
|
|
|
|
|
static unsigned char sw, sh; /* screen width, height */ |
|
|
|
static int rows; /* text rows = sh - 1 (last is status) */ |
|
|
|
static int ltop, lleft, lcy, lcx; |
|
|
|
|
|
|
|
/* ------------------------------------------------------------------ screen */ |
|
|
|
|
|
|
|
static void putstr(int r, int c, const char *s) |
|
|
|
{ |
|
|
|
gotoxy(c, r); |
|
|
|
while (*s && c < sw) { |
|
|
|
cputc(*s++); |
|
|
|
c++; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
static char lstat[84]; |
|
|
|
|
|
|
|
static void clrstat(int c) |
|
|
|
{ |
|
|
|
lstat[0] = 0; /* status row no longer as drawn */ |
|
|
|
/* the very last cell is left alone: writing it can make the screen |
|
|
|
editor wrap */ |
|
|
|
gotoxy(c, sh - 1); |
|
|
|
while (c < sw - 1) { |
|
|
|
cputc(' '); |
|
|
|
c++; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
static void sinit(void) |
|
|
|
{ |
|
|
|
screensize(&sw, &sh); |
|
|
|
rows = sh - 1; |
|
|
|
cbm_k_bsout(14); /* lower/upper case character set */ |
|
|
|
cbm_k_bsout(8); /* ...and lock it against SHIFT+C= */ |
|
|
|
bgcolor(COLOR_BLUE); |
|
|
|
bordercolor(COLOR_BLACK); |
|
|
|
textcolor(COLOR_WHITE); |
|
|
|
cursor(0); |
|
|
|
revers(0); |
|
|
|
clrscr(); |
|
|
|
if (sw > 40) fast(); /* 2 MHz: only with the 80 column screen, |
|
|
|
the VIC display is blanked in fast mode */ |
|
|
|
} |
|
|
|
|
|
|
|
static void sdone(void) |
|
|
|
{ |
|
|
|
slow(); |
|
|
|
revers(0); |
|
|
|
bgcolor(COLOR_BLACK); |
|
|
|
bordercolor(COLOR_LIGHTBLUE); |
|
|
|
textcolor(COLOR_LIGHTGREEN); |
|
|
|
clrscr(); |
|
|
|
cursor(1); |
|
|
|
} |
|
|
|
|
|
|
|
static int skey(void) |
|
|
|
{ |
|
|
|
return cgetc(); |
|
|
|
} |
|
|
|
|
|
|
|
/* --------------------------------------------------------------- buffer */ |
|
|
|
|
|
|
|
/* end offset (exclusive of the $0d) of line ln */ |
|
|
|
static unsigned linend(int ln) |
|
|
|
{ |
|
|
|
if (ln + 1 < nlines) return lptr[ln + 1] - 1; |
|
|
|
return blen; |
|
|
|
} |
|
|
|
|
|
|
|
static int linelen(int ln) |
|
|
|
{ |
|
|
|
return (int) (linend(ln) - lptr[ln]); |
|
|
|
} |
|
|
|
|
|
|
|
static void clampx(void) |
|
|
|
{ |
|
|
|
int len = linelen(cy); |
|
|
|
if (cx > len) cx = len; |
|
|
|
if (cx < 0) cx = 0; |
|
|
|
} |
|
|
|
|
|
|
|
static void buildidx(void) |
|
|
|
{ |
|
|
|
unsigned i; |
|
|
|
|
|
|
|
nlines = 1; |
|
|
|
lptr[0] = 0; |
|
|
|
for (i = 0; i < blen; i++) { |
|
|
|
if (bfr[i] == 13 && nlines < MAXLN - 1) lptr[nlines++] = i + 1; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
static void adjust(int from, int delta) /* lptr[from..nlines-1] += delta */ |
|
|
|
{ |
|
|
|
int i; |
|
|
|
for (i = from; i < nlines; i++) lptr[i] += delta; |
|
|
|
} |
|
|
|
|
|
|
|
static void bmove(unsigned from, unsigned to, unsigned n) |
|
|
|
{ |
|
|
|
unsigned char *s, *d; |
|
|
|
|
|
|
|
if (n == 0 || from == to) return; |
|
|
|
s = bfr + from; |
|
|
|
d = bfr + to; |
|
|
|
if (d < s) { |
|
|
|
while (n--) *d++ = *s++; |
|
|
|
} else { |
|
|
|
s += n; |
|
|
|
d += n; |
|
|
|
while (n--) *--d = *--s; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
static int bins(unsigned pos, int ch) |
|
|
|
{ |
|
|
|
if (blen >= BUFMAX - 1) return 0; |
|
|
|
bmove(pos, pos + 1, blen - pos); |
|
|
|
bfr[pos] = ch; |
|
|
|
blen++; |
|
|
|
dirty = 1; |
|
|
|
return 1; |
|
|
|
} |
|
|
|
|
|
|
|
static void delrange(unsigned pos, unsigned n) |
|
|
|
{ |
|
|
|
bmove(pos + n, pos, blen - pos - n); |
|
|
|
blen -= n; |
|
|
|
dirty = 1; |
|
|
|
} |
|
|
|
|
|
|
|
static int inschar(int ch) |
|
|
|
{ |
|
|
|
if (linelen(cy) >= MAXCOL) { |
|
|
|
note = "line full (80)"; |
|
|
|
return 0; |
|
|
|
} |
|
|
|
if (!bins(lptr[cy] + cx, ch)) return 0; |
|
|
|
adjust(cy + 1, 1); |
|
|
|
cx++; |
|
|
|
if (redraw < 1) redraw = 1; |
|
|
|
return 1; |
|
|
|
} |
|
|
|
|
|
|
|
static int newline(void) |
|
|
|
{ |
|
|
|
unsigned pos; |
|
|
|
int i; |
|
|
|
|
|
|
|
if (nlines >= MAXLN - 1) return 0; |
|
|
|
pos = lptr[cy] + cx; |
|
|
|
if (!bins(pos, 13)) return 0; |
|
|
|
for (i = nlines; i > cy + 1; i--) lptr[i] = lptr[i - 1]; |
|
|
|
lptr[cy + 1] = pos + 1; |
|
|
|
nlines++; |
|
|
|
adjust(cy + 2, 1); |
|
|
|
redraw = 2; |
|
|
|
cy++; |
|
|
|
cx = 0; |
|
|
|
return 1; |
|
|
|
} |
|
|
|
|
|
|
|
static int delchar(void) |
|
|
|
{ |
|
|
|
if (cx >= linelen(cy)) return 0; |
|
|
|
delrange(lptr[cy] + cx, 1); |
|
|
|
adjust(cy + 1, -1); |
|
|
|
if (redraw < 1) redraw = 1; |
|
|
|
return 1; |
|
|
|
} |
|
|
|
|
|
|
|
static void bkspace(void) |
|
|
|
{ |
|
|
|
int pl, i; |
|
|
|
|
|
|
|
if (cx > 0) { |
|
|
|
cx--; |
|
|
|
delchar(); |
|
|
|
return; |
|
|
|
} |
|
|
|
if (cy == 0) return; |
|
|
|
pl = linelen(cy - 1); |
|
|
|
if (pl + linelen(cy) > MAXCOL) { |
|
|
|
note = "joined line would pass 80"; |
|
|
|
return; |
|
|
|
} |
|
|
|
delrange(lptr[cy] - 1, 1); |
|
|
|
for (i = cy; i < nlines - 1; i++) lptr[i] = lptr[i + 1]; |
|
|
|
nlines--; |
|
|
|
adjust(cy, -1); |
|
|
|
redraw = 2; |
|
|
|
cy--; |
|
|
|
cx = pl; |
|
|
|
} |
|
|
|
|
|
|
|
static void delline(void) |
|
|
|
{ |
|
|
|
int i; |
|
|
|
unsigned s, e, n; |
|
|
|
|
|
|
|
s = lptr[cy]; |
|
|
|
e = linend(cy); |
|
|
|
ylen = e - s; |
|
|
|
if (ylen > MAXCOL) ylen = MAXCOL; |
|
|
|
for (i = 0; i < ylen; i++) ybuf[i] = bfr[s + i]; |
|
|
|
yok = 1; |
|
|
|
|
|
|
|
if (nlines == 1) { |
|
|
|
blen = 0; |
|
|
|
dirty = 1; |
|
|
|
} else if (cy + 1 < nlines) { |
|
|
|
n = lptr[cy + 1] - s; |
|
|
|
delrange(s, n); |
|
|
|
for (i = cy; i < nlines - 1; i++) lptr[i] = lptr[i + 1]; |
|
|
|
nlines--; |
|
|
|
adjust(cy, -(int) n); |
|
|
|
} else { /* last line: take its leading $0d */ |
|
|
|
delrange(s - 1, blen - s + 1); |
|
|
|
nlines--; |
|
|
|
cy--; |
|
|
|
} |
|
|
|
cx = 0; |
|
|
|
redraw = 2; |
|
|
|
} |
|
|
|
|
|
|
|
static void paste(void) |
|
|
|
{ |
|
|
|
int i; |
|
|
|
|
|
|
|
if (!yok) return; |
|
|
|
cx = linelen(cy); |
|
|
|
if (!newline()) return; |
|
|
|
for (i = 0; i < ylen; i++) inschar(ybuf[i]); |
|
|
|
cx = 0; |
|
|
|
} |
|
|
|
|
|
|
|
/* cursor keys, the same in both modes. 1 if k was one of them */ |
|
|
|
static int arrow(int k) |
|
|
|
{ |
|
|
|
if (k == K_DOWN) { |
|
|
|
if (cy + 1 < nlines) { cy++; clampx(); } |
|
|
|
return 1; |
|
|
|
} |
|
|
|
if (k == K_UP) { |
|
|
|
if (cy > 0) { cy--; clampx(); } |
|
|
|
return 1; |
|
|
|
} |
|
|
|
if (k == K_RIGHT) { |
|
|
|
cx++; |
|
|
|
clampx(); |
|
|
|
return 1; |
|
|
|
} |
|
|
|
if (k == K_LEFT) { |
|
|
|
if (cx > 0) cx--; |
|
|
|
return 1; |
|
|
|
} |
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
/* ------------------------------------------------------------------- view */ |
|
|
|
|
|
|
|
static void scroll(void) |
|
|
|
{ |
|
|
|
if (cy < top) top = cy; |
|
|
|
if (cy > top + rows - 1) top = cy - rows + 1; |
|
|
|
if (top < 0) top = 0; |
|
|
|
if (cx < leftcol) leftcol = cx; |
|
|
|
if (cx > leftcol + sw - 1) leftcol = cx - sw + 1; |
|
|
|
if (leftcol < 0) leftcol = 0; |
|
|
|
} |
|
|
|
|
|
|
|
static unsigned char rlen[50]; /* columns drawn on each row last time */ |
|
|
|
|
|
|
|
/* the character shown at screen row r, column col (blank past the line) */ |
|
|
|
static int cellch(int r, int col) |
|
|
|
{ |
|
|
|
int line = top + r; |
|
|
|
unsigned p; |
|
|
|
int ch = ' '; |
|
|
|
|
|
|
|
if (line < nlines) { |
|
|
|
p = lptr[line] + leftcol + col; |
|
|
|
if (p < linend(line)) ch = bfr[p]; |
|
|
|
} |
|
|
|
if (ch < 32 || (ch >= 128 && ch < 160)) ch = ' '; |
|
|
|
return ch; |
|
|
|
} |
|
|
|
|
|
|
|
/* repaint row r from column 'from' to the end of the text, plus whatever old |
|
|
|
text must be blanked. Only the cells that can have changed are written: |
|
|
|
each character costs a real VDC/VIC access. */ |
|
|
|
static void drawrow(int r, int from) |
|
|
|
{ |
|
|
|
int line = top + r, vl = 0, col, end; |
|
|
|
|
|
|
|
if (line < nlines) { |
|
|
|
vl = linelen(line) - leftcol; |
|
|
|
if (vl < 0) vl = 0; |
|
|
|
if (vl > sw) vl = sw; |
|
|
|
} |
|
|
|
end = vl > rlen[r] ? vl : rlen[r]; |
|
|
|
if (from < end) { |
|
|
|
gotoxy(from, r); |
|
|
|
for (col = from; col < end; col++) cputc(cellch(r, col)); |
|
|
|
} |
|
|
|
rlen[r] = vl; |
|
|
|
} |
|
|
|
|
|
|
|
static void drawcell(int r, int col, int rev) |
|
|
|
{ |
|
|
|
if (r < 0 || r >= rows || col < 0 || col >= sw) return; |
|
|
|
if (rev) revers(1); |
|
|
|
cputcxy(col, r, cellch(r, col)); |
|
|
|
if (rev) revers(0); |
|
|
|
} |
|
|
|
|
|
|
|
static char *dnum(char *t, int v) |
|
|
|
{ |
|
|
|
char d[6]; |
|
|
|
int n = 0; |
|
|
|
|
|
|
|
if (v == 0) { |
|
|
|
*t++ = '0'; |
|
|
|
return t; |
|
|
|
} |
|
|
|
while (v > 0 && n < 5) { |
|
|
|
d[n++] = '0' + (v % 10); |
|
|
|
v /= 10; |
|
|
|
} |
|
|
|
while (n > 0) *t++ = d[--n]; |
|
|
|
return t; |
|
|
|
} |
|
|
|
|
|
|
|
static void dstat(void) |
|
|
|
{ |
|
|
|
char t[84]; |
|
|
|
const char *s; |
|
|
|
char *p; |
|
|
|
int i, n, pos; |
|
|
|
|
|
|
|
if (note) s = note; |
|
|
|
else if (mode) s = "-- insert --"; |
|
|
|
else if (fname[0] == 0) s = "[no name]"; |
|
|
|
else s = fname; |
|
|
|
|
|
|
|
n = 0; |
|
|
|
for (i = 0; s[i] && i < sw - 14; i++) t[n++] = s[i]; |
|
|
|
pos = sw - 14; /* modified flag */ |
|
|
|
while (n < pos) t[n++] = ' '; |
|
|
|
t[n++] = dirty ? '+' : ' '; |
|
|
|
t[n++] = ' '; |
|
|
|
p = dnum(t + n, cy + 1); |
|
|
|
*p++ = '/'; |
|
|
|
p = dnum(p, nlines); |
|
|
|
while (p < t + sw - 1) *p++ = ' '; |
|
|
|
*p = 0; |
|
|
|
|
|
|
|
if (strcmp(t, lstat) == 0) return; /* unchanged: skip the writes */ |
|
|
|
strcpy(lstat, t); |
|
|
|
revers(1); |
|
|
|
putstr(sh - 1, 0, t); |
|
|
|
revers(0); |
|
|
|
} |
|
|
|
|
|
|
|
static void dall(void) |
|
|
|
{ |
|
|
|
int crow = cy - top; |
|
|
|
int ccol = cx - leftcol; |
|
|
|
int r, from; |
|
|
|
|
|
|
|
if (redraw == 2 || top != ltop || leftcol != lleft) { |
|
|
|
for (r = 0; r < rows; r++) drawrow(r, 0); |
|
|
|
} else { |
|
|
|
/* put the old cursor cell back to normal, then repaint what the edit |
|
|
|
touched: the cursor row from the old cursor column on */ |
|
|
|
drawcell(lcy - top, lcx - lleft, 0); |
|
|
|
from = lcx - lleft; |
|
|
|
if (ccol < from) from = ccol; |
|
|
|
if (from < 0) from = 0; |
|
|
|
if (redraw == 1) drawrow(crow, from); |
|
|
|
} |
|
|
|
drawcell(crow, ccol, 1); |
|
|
|
if (crow >= 0 && crow < rows && ccol >= rlen[crow] && ccol < sw) |
|
|
|
rlen[crow] = ccol + 1; /* a cursor past the text must be blanked later */ |
|
|
|
ltop = top; |
|
|
|
lleft = leftcol; |
|
|
|
lcy = cy; |
|
|
|
lcx = cx; |
|
|
|
redraw = 0; |
|
|
|
dstat(); |
|
|
|
} |
|
|
|
|
|
|
|
/* ------------------------------------------------------------ prompt/disk */ |
|
|
|
|
|
|
|
/* read a line of PETSCII into s, echoed from column c of the status row */ |
|
|
|
static int getln(int c, char *s, int max) |
|
|
|
{ |
|
|
|
int n = 0, k; |
|
|
|
|
|
|
|
s[0] = 0; |
|
|
|
for (;;) { |
|
|
|
k = skey(); |
|
|
|
if (k == K_RET) break; |
|
|
|
if (k == K_STOP || k == K_ESC) { |
|
|
|
n = 0; |
|
|
|
s[0] = 0; |
|
|
|
break; |
|
|
|
} |
|
|
|
if (k == K_DEL) { |
|
|
|
if (n > 0) s[--n] = 0; |
|
|
|
} else { |
|
|
|
if (k >= 0xc1 && k <= 0xda) k -= 0x80; |
|
|
|
if (k >= 32 && k < 127 && n < max) { |
|
|
|
s[n++] = k; |
|
|
|
s[n] = 0; |
|
|
|
} |
|
|
|
} |
|
|
|
clrstat(c); |
|
|
|
putstr(sh - 1, c, s); |
|
|
|
} |
|
|
|
return n; |
|
|
|
} |
|
|
|
|
|
|
|
static int askyn(const char *q) |
|
|
|
{ |
|
|
|
int k; |
|
|
|
|
|
|
|
clrstat(0); |
|
|
|
putstr(sh - 1, 0, q); |
|
|
|
for (;;) { |
|
|
|
k = skey(); |
|
|
|
if (k == KL('y') || k == KU('Y')) return 1; |
|
|
|
if (k == KL('n') || k == KU('N') || k == K_STOP || k == K_ESC) return 0; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
static void askname(void) |
|
|
|
{ |
|
|
|
clrstat(0); |
|
|
|
putstr(sh - 1, 0, "file: "); |
|
|
|
getln(6, fname, NAMEMAX); |
|
|
|
} |
|
|
|
|
|
|
|
/* the drive's error channel (open as file 15): the two digit code of the last |
|
|
|
command. 0-19 are fine, 62 is file not found. open of a missing file |
|
|
|
succeeds on a CBM drive - only this channel tells. */ |
|
|
|
static int dosst(void) |
|
|
|
{ |
|
|
|
unsigned char ch; |
|
|
|
int n = 0, d = 0; |
|
|
|
|
|
|
|
for (;;) { |
|
|
|
if (cbm_read(15, &ch, 1) != 1) break; |
|
|
|
if (ch == 13) break; |
|
|
|
if (d < 2 && ch >= '0' && ch <= '9') { |
|
|
|
n = n * 10 + ch - '0'; |
|
|
|
d++; |
|
|
|
} |
|
|
|
} |
|
|
|
if (d < 2) return 99; |
|
|
|
return n; |
|
|
|
} |
|
|
|
|
|
|
|
/* build "name,s,r" (or "@0:name,s,w") in PETSCII */ |
|
|
|
static void mkname(char *on, int save) |
|
|
|
{ |
|
|
|
int i = 0, j = 0; |
|
|
|
|
|
|
|
if (save) { |
|
|
|
on[i++] = '@'; |
|
|
|
on[i++] = '0'; |
|
|
|
on[i++] = ':'; |
|
|
|
} |
|
|
|
while (fname[j] && j < NAMEMAX) on[i++] = fname[j++]; |
|
|
|
on[i++] = ','; |
|
|
|
on[i++] = 0x53; /* PETSCII S, not ASCII */ |
|
|
|
on[i++] = ','; |
|
|
|
on[i++] = save ? 0x57 : 0x52; /* W or R */ |
|
|
|
on[i] = 0; |
|
|
|
} |
|
|
|
|
|
|
|
/* read fname into bfr. 1 on success, 0 if the file is not there. |
|
|
|
lines longer than 80 are split - the editor's line limit */ |
|
|
|
static int loadf2(void); |
|
|
|
static int loadf(void) |
|
|
|
{ |
|
|
|
int r; |
|
|
|
slow(); |
|
|
|
r = loadf2(); |
|
|
|
if (sw > 40) fast(); |
|
|
|
return r; |
|
|
|
} |
|
|
|
|
|
|
|
static int loadf2(void) |
|
|
|
{ |
|
|
|
unsigned char tmp[64]; |
|
|
|
char on[32]; |
|
|
|
int n, i, ch, c, prev = 0, col = 0; |
|
|
|
|
|
|
|
if (fname[0] == 0) return 1; |
|
|
|
mkname(on, 0); |
|
|
|
|
|
|
|
if (cbm_open(15, dev, 15, "") != 0) return 0; |
|
|
|
if (cbm_open(5, dev, 2, on) != 0) { |
|
|
|
cbm_close(15); |
|
|
|
return 0; |
|
|
|
} |
|
|
|
if (dosst() >= 20) { |
|
|
|
cbm_close(5); |
|
|
|
cbm_close(15); |
|
|
|
return 0; |
|
|
|
} |
|
|
|
blen = 0; |
|
|
|
split = 0; |
|
|
|
for (;;) { |
|
|
|
n = cbm_read(5, tmp, sizeof tmp); |
|
|
|
if (n <= 0) break; |
|
|
|
for (i = 0; i < n; i++) { |
|
|
|
ch = tmp[i]; |
|
|
|
if (ch == 26) goto done; |
|
|
|
if (ch == 10 && prev == 13) { /* CRLF seen as two bytes */ |
|
|
|
prev = ch; |
|
|
|
continue; |
|
|
|
} |
|
|
|
c = (ch == 10) ? 13 : ch; |
|
|
|
if (c != 13 && col >= MAXCOL) { |
|
|
|
if (blen < BUFMAX - 1) bfr[blen++] = 13; |
|
|
|
col = 0; |
|
|
|
split = 1; |
|
|
|
} |
|
|
|
if (blen < BUFMAX - 1) bfr[blen++] = c; |
|
|
|
col = (c == 13) ? 0 : col + 1; |
|
|
|
prev = ch; |
|
|
|
} |
|
|
|
} |
|
|
|
done: |
|
|
|
cbm_close(5); |
|
|
|
cbm_close(15); /* last: it closes everything */ |
|
|
|
return 1; |
|
|
|
} |
|
|
|
|
|
|
|
/* write the buffer to fname, replacing any old copy. 1 on success */ |
|
|
|
static int savef2(void); |
|
|
|
static int savef(void) |
|
|
|
{ |
|
|
|
int r; |
|
|
|
slow(); |
|
|
|
r = savef2(); |
|
|
|
if (sw > 40) fast(); |
|
|
|
return r; |
|
|
|
} |
|
|
|
|
|
|
|
static int savef2(void) |
|
|
|
{ |
|
|
|
char on[32]; |
|
|
|
unsigned char cr = 13; |
|
|
|
int ok; |
|
|
|
|
|
|
|
mkname(on, 1); |
|
|
|
if (cbm_open(15, dev, 15, "") != 0) return 0; |
|
|
|
if (cbm_open(5, dev, 3, on) != 0) { |
|
|
|
cbm_close(15); |
|
|
|
return 0; |
|
|
|
} |
|
|
|
if (blen > 0) cbm_write(5, bfr, blen); |
|
|
|
if (blen > 0 && bfr[blen - 1] != 13) cbm_write(5, &cr, 1); |
|
|
|
ok = (dosst() < 20); |
|
|
|
cbm_close(5); |
|
|
|
cbm_close(15); |
|
|
|
return ok; |
|
|
|
} |
|
|
|
|
|
|
|
/* the ":" command line. returns 1 when the editor should quit */ |
|
|
|
static int doscmd(void) |
|
|
|
{ |
|
|
|
char cmd[24]; |
|
|
|
int n, p = 1, ok = 0, qf = 0; |
|
|
|
|
|
|
|
clrstat(0); |
|
|
|
putstr(sh - 1, 0, ":"); |
|
|
|
n = getln(1, cmd, 20); |
|
|
|
clrstat(0); |
|
|
|
if (n == 0) return 0; |
|
|
|
|
|
|
|
if (cmd[0] == KL('q')) { |
|
|
|
if (cmd[1] == '!') return 1; |
|
|
|
if (dirty) { |
|
|
|
note = "unsaved - :q! quits"; |
|
|
|
return 0; |
|
|
|
} |
|
|
|
return 1; |
|
|
|
} |
|
|
|
if (cmd[0] == KL('w')) ok = 1; |
|
|
|
if (cmd[0] == KL('x')) { |
|
|
|
ok = 1; |
|
|
|
qf = 1; |
|
|
|
} |
|
|
|
if (!ok) { |
|
|
|
note = "unknown command"; |
|
|
|
return 0; |
|
|
|
} |
|
|
|
if (cmd[0] == KL('w') && cmd[1] == KL('q')) { |
|
|
|
qf = 1; |
|
|
|
p = 2; |
|
|
|
} |
|
|
|
while (cmd[p] == ' ') p++; |
|
|
|
if (cmd[p]) { |
|
|
|
n = 0; |
|
|
|
while (cmd[p] && n < NAMEMAX) fname[n++] = cmd[p++]; |
|
|
|
fname[n] = 0; |
|
|
|
} |
|
|
|
if (fname[0] == 0) { |
|
|
|
note = "no file name"; |
|
|
|
return 0; |
|
|
|
} |
|
|
|
if (savef()) { |
|
|
|
dirty = 0; |
|
|
|
note = "written"; |
|
|
|
return qf; |
|
|
|
} |
|
|
|
note = "cannot write"; |
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
/* ------------------------------------------------------------------- main */ |
|
|
|
|
|
|
|
int main(void) |
|
|
|
{ |
|
|
|
int k, ok, p; |
|
|
|
|
|
|
|
dev = *(unsigned char *) 0x00ba; /* device we were loaded from */ |
|
|
|
if (dev < 8) dev = 8; |
|
|
|
|
|
|
|
sinit(); |
|
|
|
|
|
|
|
blen = 0; |
|
|
|
nlines = 1; |
|
|
|
lptr[0] = 0; |
|
|
|
cy = cx = top = leftcol = 0; |
|
|
|
fname[0] = 0; |
|
|
|
mode = dirty = pend = ylen = yok = split = 0; |
|
|
|
note = 0; |
|
|
|
redraw = 2; |
|
|
|
ltop = lleft = lcy = 0; |
|
|
|
|
|
|
|
for (;;) { /* a file to edit */ |
|
|
|
askname(); |
|
|
|
if (fname[0] == 0) { /* no name: an empty buffer */ |
|
|
|
ok = 1; |
|
|
|
break; |
|
|
|
} |
|
|
|
ok = loadf(); |
|
|
|
if (ok) break; |
|
|
|
if (askyn("not found - create it? (y/n) ")) break; |
|
|
|
} |
|
|
|
buildidx(); |
|
|
|
if (!ok) { /* keep the name: :w creates it */ |
|
|
|
nlines = 1; |
|
|
|
blen = 0; |
|
|
|
note = "new file"; |
|
|
|
} |
|
|
|
if (split) note = "long lines split at 80"; |
|
|
|
clrscr(); |
|
|
|
memset(rlen, 0, sizeof rlen); |
|
|
|
lstat[0] = 0; |
|
|
|
|
|
|
|
for (;;) { |
|
|
|
scroll(); |
|
|
|
dall(); |
|
|
|
k = skey(); |
|
|
|
note = 0; |
|
|
|
|
|
|
|
if (mode) { /* ---- insert mode */ |
|
|
|
if (k == K_STOP || k == K_ESC) { |
|
|
|
mode = 0; |
|
|
|
if (cx > 0) cx--; |
|
|
|
} else if (k == K_RET) { |
|
|
|
newline(); |
|
|
|
} else if (k == K_DEL) { |
|
|
|
bkspace(); |
|
|
|
} else if (arrow(k)) { |
|
|
|
; |
|
|
|
} else if ((k >= 32 && k < 128) || k >= 161) { |
|
|
|
inschar(k); |
|
|
|
} |
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
if (pend) { /* ---- second key of dd / gg */ |
|
|
|
p = pend; |
|
|
|
pend = 0; |
|
|
|
if (p == KL('d') && k == KL('d')) delline(); |
|
|
|
if (p == KL('g') && k == KL('g')) { |
|
|
|
cy = 0; |
|
|
|
cx = 0; |
|
|
|
} |
|
|
|
clampx(); |
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
if (arrow(k)) { |
|
|
|
; |
|
|
|
} else if (k == KL('h')) { |
|
|
|
if (cx > 0) cx--; |
|
|
|
} else if (k == KL('l')) { |
|
|
|
cx++; |
|
|
|
clampx(); |
|
|
|
} else if (k == KL('j')) { |
|
|
|
arrow(K_DOWN); |
|
|
|
} else if (k == KL('k')) { |
|
|
|
arrow(K_UP); |
|
|
|
} else if (k == '0') { |
|
|
|
cx = 0; |
|
|
|
} else if (k == '$') { |
|
|
|
cx = linelen(cy); |
|
|
|
} else if (k == KU('G')) { |
|
|
|
cy = nlines - 1; |
|
|
|
cx = 0; |
|
|
|
} else if (k == KL('d')) { |
|
|
|
pend = KL('d'); |
|
|
|
} else if (k == KL('g')) { |
|
|
|
pend = KL('g'); |
|
|
|
} else if (k == KL('x')) { |
|
|
|
delchar(); |
|
|
|
clampx(); |
|
|
|
} else if (k == KL('p')) { |
|
|
|
paste(); |
|
|
|
} else if (k == KL('i')) { |
|
|
|
mode = 1; |
|
|
|
} else if (k == KL('a')) { |
|
|
|
cx++; |
|
|
|
clampx(); |
|
|
|
mode = 1; |
|
|
|
} else if (k == KU('A')) { |
|
|
|
cx = linelen(cy); |
|
|
|
mode = 1; |
|
|
|
} else if (k == KU('I')) { |
|
|
|
cx = 0; |
|
|
|
mode = 1; |
|
|
|
} else if (k == KL('o')) { |
|
|
|
cx = linelen(cy); |
|
|
|
newline(); |
|
|
|
mode = 1; |
|
|
|
} else if (k == KU('O')) { |
|
|
|
cx = 0; |
|
|
|
newline(); |
|
|
|
cy--; |
|
|
|
mode = 1; |
|
|
|
} else if (k == ':') { |
|
|
|
if (doscmd() == 1) { |
|
|
|
sdone(); |
|
|
|
return 0; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |