Subversion Repositories SvarDOS

Rev

Rev 1572 | Rev 1574 | Go to most recent revision | Only display areas with differences | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 1572 Rev 1573
1
/* Sved, the SvarDOS editor
1
/* Sved, the SvarDOS editor
2
 *
2
 *
3
 * Copyright (C) 2023 Mateusz Viste
3
 * Copyright (C) 2023 Mateusz Viste
4
 *
4
 *
5
 * Sved is released under the terms of the MIT license.
5
 * Sved is released under the terms of the MIT license.
6
 *
6
 *
7
 * Permission is hereby granted, free of charge, to any person obtaining a copy
7
 * Permission is hereby granted, free of charge, to any person obtaining a copy
8
 * of this software and associated documentation files (the "Software"), to
8
 * of this software and associated documentation files (the "Software"), to
9
 * deal in the Software without restriction, including without limitation the
9
 * deal in the Software without restriction, including without limitation the
10
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
11
 * sell copies of the Software, and to permit persons to whom the Software is
11
 * sell copies of the Software, and to permit persons to whom the Software is
12
 * furnished to do so, subject to the following conditions:
12
 * furnished to do so, subject to the following conditions:
13
 *
13
 *
14
 * The above copyright notice and this permission notice shall be included in
14
 * The above copyright notice and this permission notice shall be included in
15
 * all copies or substantial portions of the Software.
15
 * all copies or substantial portions of the Software.
16
 *
16
 *
17
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
23
 * IN THE SOFTWARE.
23
 * IN THE SOFTWARE.
24
 */
24
 */
25
 
25
 
26
#include <i86.h> /* MK_FP() */
26
#include <i86.h> /* MK_FP() */
27
 
27
 
28
#include "libc.h"
28
#include "libc.h"
29
 
29
 
30
#include "mdr\bios.h"
30
#include "mdr\bios.h"
31
#include "mdr\cout.h"
31
#include "mdr\cout.h"
32
#include "mdr\dos.h"
32
#include "mdr\dos.h"
33
 
33
 
34
#include "svarlang\svarlang.h"
34
#include "svarlang\svarlang.h"
35
 
35
 
36
 
36
 
37
#define PVER "2023.5"
37
#define PVER "2023.5"
38
#define PDATE "2023"
38
#define PDATE "2023"
39
 
39
 
40
/*****************************************************************************
40
/*****************************************************************************
41
 * global variables and definitions                                          *
41
 * global variables and definitions                                          *
42
 *****************************************************************************/
42
 *****************************************************************************/
43
 
43
 
44
/* preload the mono scheme (to be overloaded at runtime if color adapter present) */
44
/* preload the mono scheme (to be overloaded at runtime if color adapter present) */
45
static unsigned char SCHEME_TEXT   = 0x07,
45
static unsigned char SCHEME_TEXT   = 0x07,
46
                     SCHEME_MENU   = 0x70,
46
                     SCHEME_MENU   = 0x70,
47
                     SCHEME_MENU_CUR= 0x0f,
47
                     SCHEME_MENU_CUR= 0x0f,
48
                     SCHEME_MENU_SEL= 0x00,
48
                     SCHEME_MENU_SEL= 0x00,
49
                     SCHEME_STBAR1 = 0x70,
49
                     SCHEME_STBAR1 = 0x70,
50
                     SCHEME_STBAR2 = 0x70, /* greyed out information */
50
                     SCHEME_STBAR2 = 0x70, /* greyed out information */
51
                     SCHEME_STBAR3 = 0x70, /* query */
51
                     SCHEME_STBAR3 = 0x70, /* query */
52
                     SCHEME_SCROLL = 0x70,
52
                     SCHEME_SCROLL = 0x70,
53
                     SCHEME_MSG    = 0x70,
53
                     SCHEME_MSG    = 0x70,
54
                     SCHEME_ERR    = 0x70;
54
                     SCHEME_ERR    = 0x70;
55
 
55
 
56
static unsigned char screenw, screenh, screenlastrow, screenlastcol;
56
static unsigned char screenw, screenh, screenlastrow, screenlastcol;
57
static unsigned char glob_monomode, glob_tablessmode;
57
static unsigned char glob_monomode, glob_tablessmode;
58
 
58
 
59
static char buff[512]; /* short lived buffer for whatever needed */
59
static char buff[512]; /* short lived buffer for whatever needed */
60
 
60
 
61
static struct {
61
static struct {
62
    unsigned char from;
62
    unsigned char from;
63
    unsigned char to;
63
    unsigned char to;
64
    unsigned char statusbar;
64
    unsigned char statusbar;
65
} uidirty = {0, 0xff, 1}; /* make sure to redraw entire UI at first run */
65
} uidirty = {0, 0xff, 1}; /* make sure to redraw entire UI at first run */
66
 
66
 
67
#define SCROLL_CURSOR 0xB1
67
#define SCROLL_CURSOR 0xB1
68
 
68
 
69
struct line {
69
struct line {
70
  unsigned short len;
70
  unsigned short len;
71
  struct line far *next;
71
  struct line far *next;
72
  struct line far *prev;
72
  struct line far *prev;
73
  char payload[1];
73
  char payload[1];
74
};
74
};
75
 
75
 
76
struct file {
76
struct file {
77
  struct line far *cursor;
77
  struct line far *cursor;
78
  unsigned short xoffset;
78
  unsigned short xoffset;
79
  unsigned short cursorposx;
79
  unsigned short cursorposx;
80
  unsigned short cursorposy;
80
  unsigned short cursorposy;
81
  unsigned short totlines;
81
  unsigned short totlines;
82
  unsigned short curline;
82
  unsigned short curline;
83
  unsigned short curline_prev;
83
  unsigned short curline_prev;
84
  char lfonly;   /* set if line endings are LF (CR/LF otherwise) */
84
  char lfonly;   /* set if line endings are LF (CR/LF otherwise) */
85
  char modflag;  /* non-zero if file has been modified since last save */
85
  char modflag;  /* non-zero if file has been modified since last save */
86
  char modflagprev;
86
  char modflagprev;
87
  char slotid; /* 0..9 */
87
  char slotid; /* 0..9 */
88
  char fname[128];
88
  char fname[128];
89
};
89
};
90
 
90
 
91
 
91
 
92
 
92
 
93
/*****************************************************************************
93
/*****************************************************************************
94
 * assembly "functions"                                                      *
94
 * assembly "functions"                                                      *
95
 *****************************************************************************/
95
 *****************************************************************************/
96
 
96
 
97
unsigned short dosalloc(unsigned short siz);
97
unsigned short dosalloc(unsigned short siz);
98
 
98
 
99
#pragma aux dosalloc = \
99
#pragma aux dosalloc = \
100
"mov ah, 0x48" \
100
"mov ah, 0x48" \
101
"int 0x21" \
101
"int 0x21" \
102
"jnc done" \
102
"jnc done" \
103
"xor ax, ax" \
103
"xor ax, ax" \
104
"done:" \
104
"done:" \
105
parm [bx] \
105
parm [bx] \
106
value [ax];
106
value [ax];
107
 
107
 
108
 
108
 
109
unsigned short dosfree(unsigned short segn);
109
unsigned short dosfree(unsigned short segn);
110
 
110
 
111
#pragma aux dosfree = \
111
#pragma aux dosfree = \
112
"mov ah, 0x49" \
112
"mov ah, 0x49" \
113
"int 0x21" \
113
"int 0x21" \
114
"jc done" \
114
"jc done" \
115
"xor ax, ax" \
115
"xor ax, ax" \
116
"done:" \
116
"done:" \
117
parm [es] \
117
parm [es] \
118
value [ax];
118
value [ax];
119
 
119
 
-
 
120
unsigned short dosfclose(unsigned short handle);
-
 
121
 
-
 
122
#pragma aux dosfclose = \
-
 
123
"mov ah,0x3e" \
-
 
124
"int 0x21" \
-
 
125
"jc done" \
-
 
126
"xor ax, ax" \
-
 
127
"done:" \
-
 
128
parm [bx] \
-
 
129
value [ax];
-
 
130
 
120
 
131
 
121
/*****************************************************************************
132
/*****************************************************************************
122
 * functions                                                                 *
133
 * functions                                                                 *
123
 *****************************************************************************/
134
 *****************************************************************************/
124
 
135
 
125
static struct line far *line_calloc(unsigned short siz) {
136
static struct line far *line_calloc(unsigned short siz) {
126
  struct line far *res;
137
  struct line far *res;
127
  unsigned short seg;
138
  unsigned short seg;
128
 
139
 
129
  seg = dosalloc((sizeof(struct line) + siz + 15) / 16);
140
  seg = dosalloc((sizeof(struct line) + siz + 15) / 16);
130
  if (seg == 0) return(NULL);
141
  if (seg == 0) return(NULL);
131
  res = MK_FP(seg, 0);
142
  res = MK_FP(seg, 0);
132
  res->len = 0;
143
  res->len = 0;
133
  res->next = NULL;
144
  res->next = NULL;
134
  res->prev = NULL;
145
  res->prev = NULL;
135
 
146
 
136
  return(res);
147
  return(res);
137
}
148
}
138
 
149
 
139
 
150
 
140
static void line_free(struct line far *ptr) {
151
static void line_free(struct line far *ptr) {
141
  dosfree(FP_SEG(ptr));
152
  dosfree(FP_SEG(ptr));
142
}
153
}
143
 
154
 
144
 
155
 
145
static int curline_resize(struct file *db, unsigned short newsiz) {
156
static int curline_resize(struct file *db, unsigned short newsiz) {
146
  struct line far *newptr;
157
  struct line far *newptr;
147
 
158
 
148
  /* try resizing the block (much faster) */
159
  /* try resizing the block (much faster) */
149
  if (mdr_dos_resizeblock((sizeof(struct line) + newsiz + 15) / 16, FP_SEG(db->cursor)) == 0) return(0);
160
  if (mdr_dos_resizeblock((sizeof(struct line) + newsiz + 15) / 16, FP_SEG(db->cursor)) == 0) return(0);
150
 
161
 
151
  /* create a new block and copy data over */
162
  /* create a new block and copy data over */
152
  newptr = line_calloc(newsiz);
163
  newptr = line_calloc(newsiz);
153
  if (newptr == NULL) return(-1);
164
  if (newptr == NULL) return(-1);
154
  fmemmove(newptr, db->cursor, sizeof(struct line) + db->cursor->len);
165
  fmemmove(newptr, db->cursor, sizeof(struct line) + db->cursor->len);
155
 
166
 
156
  /* rewire the linked list */
167
  /* rewire the linked list */
157
  db->cursor = newptr;
168
  db->cursor = newptr;
158
  if (newptr->next) newptr->next->prev = newptr;
169
  if (newptr->next) newptr->next->prev = newptr;
159
  if (newptr->prev) newptr->prev->next = newptr;
170
  if (newptr->prev) newptr->prev->next = newptr;
160
 
171
 
161
  return(0);
172
  return(0);
162
}
173
}
163
 
174
 
164
 
175
 
165
/* adds a new line at cursor position into file linked list and advance cursor
176
/* adds a new line at cursor position into file linked list and advance cursor
166
 * returns non-zero on error */
177
 * returns non-zero on error */
167
static int line_add(struct file *db, const char far *line, unsigned short slen) {
178
static int line_add(struct file *db, const char far *line, unsigned short slen) {
168
  struct line far *l;
179
  struct line far *l;
169
 
180
 
170
  l = line_calloc(slen);
181
  l = line_calloc(slen);
171
  if (l == NULL) return(-1);
182
  if (l == NULL) return(-1);
172
 
183
 
173
  l->prev = db->cursor;
184
  l->prev = db->cursor;
174
  if (db->cursor) {
185
  if (db->cursor) {
175
    l->next = db->cursor->next;
186
    l->next = db->cursor->next;
176
    db->cursor->next = l;
187
    db->cursor->next = l;
177
    l->next->prev = l;
188
    l->next->prev = l;
178
  }
189
  }
179
  db->cursor = l;
190
  db->cursor = l;
180
  if (slen > 0) {
191
  if (slen > 0) {
181
    fmemmove(l->payload, line, slen);
192
    fmemmove(l->payload, line, slen);
182
    l->len = slen;
193
    l->len = slen;
183
  }
194
  }
184
 
195
 
185
  db->totlines += 1;
196
  db->totlines += 1;
186
  db->curline += 1;
197
  db->curline += 1;
187
 
198
 
188
  return(0);
199
  return(0);
189
}
200
}
190
 
201
 
191
 
202
 
192
static void ui_getstring(const char *query, char *s, unsigned short maxlen) {
203
static void ui_getstring(const char *query, char *s, unsigned short maxlen) {
193
  unsigned short len = 0;
204
  unsigned short len = 0;
194
  unsigned char x;
205
  unsigned char x;
195
  int k;
206
  int k;
196
 
207
 
197
  if (maxlen == 0) return;
208
  if (maxlen == 0) return;
198
  maxlen--; /* make room for the nul terminator */
209
  maxlen--; /* make room for the nul terminator */
199
 
210
 
200
  /* print query string */
211
  /* print query string */
201
  x = mdr_cout_str(screenlastrow, 0, query, SCHEME_STBAR3, 40);
212
  x = mdr_cout_str(screenlastrow, 0, query, SCHEME_STBAR3, 40);
202
  mdr_cout_char_rep(screenlastrow, x++, ' ', SCHEME_STBAR3, screenw - x);
213
  mdr_cout_char_rep(screenlastrow, x++, ' ', SCHEME_STBAR3, screenw - x);
203
 
214
 
204
  for (;;) {
215
  for (;;) {
205
    mdr_cout_locate(screenlastrow, x + len);
216
    mdr_cout_locate(screenlastrow, x + len);
206
    k = mdr_dos_getkey2();
217
    k = mdr_dos_getkey2();
207
 
218
 
208
    switch (k) {
219
    switch (k) {
209
      case 0x1b: /* ESC */
220
      case 0x1b: /* ESC */
210
        s[0] = 0;
221
        s[0] = 0;
211
        return;
222
        return;
212
      case '\r':
223
      case '\r':
213
        s[len] = 0;
224
        s[len] = 0;
214
        return;
225
        return;
215
      case 0x08: /* BKSPC */
226
      case 0x08: /* BKSPC */
216
        if (len > 0) {
227
        if (len > 0) {
217
          len--;
228
          len--;
218
          mdr_cout_char(screenlastrow, x + len, ' ', SCHEME_STBAR3);
229
          mdr_cout_char(screenlastrow, x + len, ' ', SCHEME_STBAR3);
219
        }
230
        }
220
        break;
231
        break;
221
      default:
232
      default:
222
        if ((k <= 0xff) && (k >= ' ') && (len < maxlen)) {
233
        if ((k <= 0xff) && (k >= ' ') && (len < maxlen)) {
223
          mdr_cout_char(screenlastrow, x + len, k, SCHEME_STBAR3);
234
          mdr_cout_char(screenlastrow, x + len, k, SCHEME_STBAR3);
224
          s[len++] = k;
235
          s[len++] = k;
225
        }
236
        }
226
    }
237
    }
227
  }
238
  }
228
 
239
 
229
}
240
}
230
 
241
 
231
 
242
 
232
/* append a nul-terminated string to line at cursor position */
243
/* append a nul-terminated string to line at cursor position */
233
static int line_append(struct file *f, const char *buf, unsigned short len) {
244
static int line_append(struct file *f, const char *buf, unsigned short len) {
234
  if (sizeof(struct line) + f->cursor->len + len < len) goto ERR; /* overflow check */
245
  if (sizeof(struct line) + f->cursor->len + len < len) goto ERR; /* overflow check */
235
  if (curline_resize(f, f->cursor->len + len) != 0) goto ERR;
246
  if (curline_resize(f, f->cursor->len + len) != 0) goto ERR;
236
 
247
 
237
  fmemmove(f->cursor->payload + f->cursor->len, buf, len);
248
  fmemmove(f->cursor->payload + f->cursor->len, buf, len);
238
  f->cursor->len += len;
249
  f->cursor->len += len;
239
 
250
 
240
  return(0);
251
  return(0);
241
  ERR:
252
  ERR:
242
  return(-1);
253
  return(-1);
243
}
254
}
244
 
255
 
245
 
256
 
246
static void db_rewind(struct file *db) {
257
static void db_rewind(struct file *db) {
247
  if (db->cursor == NULL) return;
258
  if (db->cursor == NULL) return;
248
  while (db->cursor->prev) db->cursor = db->cursor->prev;
259
  while (db->cursor->prev) db->cursor = db->cursor->prev;
249
  db->curline = 0;
260
  db->curline = 0;
250
}
261
}
251
 
262
 
252
 
263
 
253
/* saves db file, resets db->modflag on success and overwrites the filename
264
/* saves db file, resets db->modflag on success and overwrites the filename
254
 * field if saveas is not NULL */
265
 * field if saveas is not NULL */
255
static int savefile(struct file *db, const char *saveas) {
266
static int savefile(struct file *db, const char *saveas) {
256
  int fd = 0;
267
  int fd = 0;
257
  const struct line far *l;
268
  const struct line far *l;
258
  unsigned short bytes;
269
  unsigned short bytes;
259
  unsigned char eollen = 2;
270
  unsigned char eollen = 2;
260
  const unsigned char *eolbuf = "\r\n";
271
  const unsigned char *eolbuf = "\r\n";
261
  int errflag = 0;
272
  int errflag = 0;
262
 
273
 
263
  /* if filename not overloaded then use the fname in db */
274
  /* if filename not overloaded then use the fname in db */
264
  if (saveas == NULL) saveas = db->fname;
275
  if (saveas == NULL) saveas = db->fname;
265
 
276
 
266
  _asm {
277
  _asm {
267
    push ax
278
    push ax
268
    push cx
279
    push cx
269
    push dx
280
    push dx
270
 
281
 
271
    mov ah, 0x3C    /* create or truncate file */
282
    mov ah, 0x3C    /* create or truncate file */
272
    xor cx, cx      /* file attributes */
283
    xor cx, cx      /* file attributes */
273
    mov dx, saveas  /* works only in SMALL/TINY mode */
284
    mov dx, saveas  /* works only in SMALL/TINY mode */
274
    int 0x21
285
    int 0x21
275
    jnc DONE
286
    jnc DONE
276
    mov errflag, ax
287
    mov errflag, ax
277
    DONE:
288
    DONE:
278
    mov fd, ax
289
    mov fd, ax
279
 
290
 
280
    pop dx
291
    pop dx
281
    pop cx
292
    pop cx
282
    pop ax
293
    pop ax
283
  }
294
  }
284
 
295
 
285
  if (errflag != 0) return(-1);
296
  if (errflag != 0) return(-1);
286
 
297
 
287
  l = db->cursor;
298
  l = db->cursor;
288
  while (l->prev) l = l->prev;
299
  while (l->prev) l = l->prev;
289
 
300
 
290
  /* preset line terminators */
301
  /* preset line terminators */
291
  if (db->lfonly) {
302
  if (db->lfonly) {
292
    eolbuf++;
303
    eolbuf++;
293
    eollen--;
304
    eollen--;
294
  }
305
  }
295
 
306
 
296
  while (l) {
307
  while (l) {
297
    /* do not write the last empty line, it is only useful for edition */
308
    /* do not write the last empty line, it is only useful for edition */
298
    if (l->len != 0) {
309
    if (l->len != 0) {
299
      errflag |= mdr_dos_write(fd, l->payload, l->len, &bytes);
310
      errflag |= mdr_dos_write(fd, l->payload, l->len, &bytes);
300
    } else if (l->next == NULL) {
311
    } else if (l->next == NULL) {
301
      break;
312
      break;
302
    }
313
    }
303
    errflag |= mdr_dos_write(fd, eolbuf, eollen, &bytes);
314
    errflag |= mdr_dos_write(fd, eolbuf, eollen, &bytes);
304
    l = l->next;
315
    l = l->next;
305
  }
316
  }
306
 
317
 
307
  errflag |= mdr_dos_fclose(fd);
318
  errflag |= dosfclose(fd);
308
 
319
 
309
  /* did it all work? */
320
  /* did it all work? */
310
  if (errflag == 0) {
321
  if (errflag == 0) {
311
    db->modflag = 0;
322
    db->modflag = 0;
312
    if (saveas != db->fname) mdr_dos_truename(db->fname, saveas);
323
    if (saveas != db->fname) mdr_dos_truename(db->fname, saveas);
313
  }
324
  }
314
 
325
 
315
  return(errflag);
326
  return(errflag);
316
}
327
}
317
 
328
 
318
 
329
 
319
static void ui_statusbar(const struct file *db) {
330
static void ui_statusbar(const struct file *db) {
320
  const char *s = svarlang_strid(0); /* ESC=MENU */
331
  const char *s = svarlang_strid(0); /* ESC=MENU */
321
  unsigned short helpcol = screenw - strlen(s);
332
  unsigned short helpcol = screenw - strlen(s);
322
  unsigned short col;
333
  unsigned short col;
323
 
334
 
324
  /* slot number (guaranteed to be 0-9) */
335
  /* slot number (guaranteed to be 0-9) */
325
  {
336
  {
326
    char slot[4] = "#00";
337
    char slot[4] = "#00";
327
    if (db->slotid == 9) {
338
    if (db->slotid == 9) {
328
      slot[1] = '1';
339
      slot[1] = '1';
329
    } else {
340
    } else {
330
      slot[2] += db->slotid + 1;
341
      slot[2] += db->slotid + 1;
331
    }
342
    }
332
    mdr_cout_str(screenlastrow, 0, slot, SCHEME_STBAR2, 3);
343
    mdr_cout_str(screenlastrow, 0, slot, SCHEME_STBAR2, 3);
333
  }
344
  }
334
 
345
 
335
  /* fill rest of status bar with background */
346
  /* fill rest of status bar with background */
336
  mdr_cout_char_rep(screenlastrow, 3, ' ', SCHEME_STBAR1, helpcol - 3);
347
  mdr_cout_char_rep(screenlastrow, 3, ' ', SCHEME_STBAR1, helpcol - 3);
337
 
348
 
338
  /* eol type */
349
  /* eol type */
339
  {
350
  {
340
    const char *eoltype = "CRLF";
351
    const char *eoltype = "CRLF";
341
    if (db->lfonly) eoltype += 2;
352
    if (db->lfonly) eoltype += 2;
342
    mdr_cout_str(screenlastrow, helpcol - 5, eoltype, SCHEME_STBAR1, 5);
353
    mdr_cout_str(screenlastrow, helpcol - 5, eoltype, SCHEME_STBAR1, 5);
343
  }
354
  }
344
 
355
 
345
  /* line numbers */
356
  /* line numbers */
346
  {
357
  {
347
    unsigned short x;
358
    unsigned short x;
348
    unsigned char count = 0;
359
    unsigned char count = 0;
349
    col = helpcol - 7;
360
    col = helpcol - 7;
350
 
361
 
351
    x = db->totlines;
362
    x = db->totlines;
352
    AGAIN:
363
    AGAIN:
353
    do {
364
    do {
354
      mdr_cout_char(screenlastrow, col--, '0' + (x % 10), SCHEME_STBAR1);
365
      mdr_cout_char(screenlastrow, col--, '0' + (x % 10), SCHEME_STBAR1);
355
      x /= 10;
366
      x /= 10;
356
    } while (x);
367
    } while (x);
357
    /* redo same exercise, but printing the current line now */
368
    /* redo same exercise, but printing the current line now */
358
    if (count == 0) {
369
    if (count == 0) {
359
      count = 1;
370
      count = 1;
360
      mdr_cout_char(screenlastrow, col--, '/', SCHEME_STBAR1);
371
      mdr_cout_char(screenlastrow, col--, '/', SCHEME_STBAR1);
361
      x = 1 + db->curline;
372
      x = 1 + db->curline;
362
      goto AGAIN;
373
      goto AGAIN;
363
    }
374
    }
364
  }
375
  }
365
 
376
 
366
  /* filename and modflag */
377
  /* filename and modflag */
367
  {
378
  {
368
    const char *fn;
379
    const char *fn;
369
    unsigned short x;
380
    unsigned short x;
370
    unsigned short maxfnlen = col - 6;
381
    unsigned short maxfnlen = col - 6;
371
    if (db->fname[0] == 0) {
382
    if (db->fname[0] == 0) {
372
      fn = svarlang_str(0, 1); /* "UNTITLED" */
383
      fn = svarlang_str(0, 1); /* "UNTITLED" */
373
    } else {
384
    } else {
374
      /* display filename up to maxfnlen chars */
385
      /* display filename up to maxfnlen chars */
375
      fn = db->fname;
386
      fn = db->fname;
376
      x = strlen(fn);
387
      x = strlen(fn);
377
      if (x > maxfnlen) fn += x - maxfnlen;
388
      if (x > maxfnlen) fn += x - maxfnlen;
378
    }
389
    }
379
    x = mdr_cout_str(screenlastrow, 4, fn, SCHEME_STBAR1, maxfnlen);
390
    x = mdr_cout_str(screenlastrow, 4, fn, SCHEME_STBAR1, maxfnlen);
380
    if (db->modflag) mdr_cout_char(screenlastrow, 5 + x, '!', SCHEME_STBAR2);
391
    if (db->modflag) mdr_cout_char(screenlastrow, 5 + x, '!', SCHEME_STBAR2);
381
  }
392
  }
382
 
393
 
383
  mdr_cout_str(screenlastrow, helpcol, s, SCHEME_STBAR2, 40);
394
  mdr_cout_str(screenlastrow, helpcol, s, SCHEME_STBAR2, 40);
384
}
395
}
385
 
396
 
386
 
397
 
387
static void ui_msg(unsigned short msgid1, unsigned short msgid2, unsigned short msgid3, unsigned char attr) {
398
static void ui_msg(unsigned short msgid1, unsigned short msgid2, unsigned short msgid3, unsigned char attr) {
388
  unsigned short x, y, maxmsglen, i;
399
  unsigned short x, y, maxmsglen, i;
389
  unsigned short msgcount = 1;
400
  unsigned short msgcount = 1;
390
  const char *msg[3];
401
  const char *msg[3];
391
 
402
 
392
  msg[0] = svarlang_strid(msgid1);
403
  msg[0] = svarlang_strid(msgid1);
393
 
404
 
394
  if (msgid2 != 0) {
405
  if (msgid2 != 0) {
395
    msgcount = 2;
406
    msgcount = 2;
396
    msg[1] = svarlang_strid(msgid2);
407
    msg[1] = svarlang_strid(msgid2);
397
  }
408
  }
398
  if (msgid3 != 0) {
409
  if (msgid3 != 0) {
399
    msgcount = 3;
410
    msgcount = 3;
400
    msg[2] = svarlang_strid(msgid3);
411
    msg[2] = svarlang_strid(msgid3);
401
  }
412
  }
402
 
413
 
403
  /* find longest msg */
414
  /* find longest msg */
404
  maxmsglen = 0;
415
  maxmsglen = 0;
405
  for (i = 0; i < msgcount; i++) {
416
  for (i = 0; i < msgcount; i++) {
406
    y = strlen(msg[i]);
417
    y = strlen(msg[i]);
407
    if (y > maxmsglen) maxmsglen = y;
418
    if (y > maxmsglen) maxmsglen = y;
408
  }
419
  }
409
 
420
 
410
  y = (screenh - 6) >> 1;
421
  y = (screenh - 6) >> 1;
411
  x = (screenw - maxmsglen - 3) >> 1;
422
  x = (screenw - maxmsglen - 3) >> 1;
412
  for (i = y+1+msgcount; i >= y; i--) mdr_cout_char_rep(i, x, ' ', attr, maxmsglen + 2);
423
  for (i = y+1+msgcount; i >= y; i--) mdr_cout_char_rep(i, x, ' ', attr, maxmsglen + 2);
413
  x++;
424
  x++;
414
 
425
 
415
  for (i = 0; i < msgcount; i++) {
426
  for (i = 0; i < msgcount; i++) {
416
    mdr_cout_str(y+1+i, x, msg[i], attr, maxmsglen);
427
    mdr_cout_str(y+1+i, x, msg[i], attr, maxmsglen);
417
  }
428
  }
418
 
429
 
419
  if (uidirty.from > y) uidirty.from = y;
430
  if (uidirty.from > y) uidirty.from = y;
420
  if (uidirty.to < y+4) uidirty.to = y+4;
431
  if (uidirty.to < y+4) uidirty.to = y+4;
421
}
432
}
422
 
433
 
423
 
434
 
424
/* returns 0 if operation may proceed, non-zero to cancel */
435
/* returns 0 if operation may proceed, non-zero to cancel */
425
static unsigned char ui_confirm_if_unsaved(struct file *db) {
436
static unsigned char ui_confirm_if_unsaved(struct file *db) {
426
  int k;
437
  int k;
427
 
438
 
428
  if (db->modflag == 0) return(0);
439
  if (db->modflag == 0) return(0);
429
 
440
 
430
  mdr_cout_cursor_hide();
441
  mdr_cout_cursor_hide();
431
 
442
 
432
  /* if file has been modified then ask for confirmation:
443
  /* if file has been modified then ask for confirmation:
433
   * ENTER        : agree to data loss
444
   * ENTER        : agree to data loss
434
   * SPACE        : SAVE file before quit (only if valid filename present)
445
   * SPACE        : SAVE file before quit (only if valid filename present)
435
   * anything else: ABORT */
446
   * anything else: ABORT */
436
  ui_msg(4, 5, (db->fname[0])?9:8, SCHEME_MSG);
447
  ui_msg(4, 5, (db->fname[0])?9:8, SCHEME_MSG);
437
 
448
 
438
  k = mdr_dos_getkey2();
449
  k = mdr_dos_getkey2();
439
  mdr_cout_cursor_show();
450
  mdr_cout_cursor_show();
440
 
451
 
441
  /* ENTER = agree to loose unsaved data */
452
  /* ENTER = agree to loose unsaved data */
442
  if (k == '\r') return(0);
453
  if (k == '\r') return(0);
443
 
454
 
444
  /* SPACE = save file and continue */
455
  /* SPACE = save file and continue */
445
  if ((k == ' ') && (db->fname[0] != 0) && (savefile(db, NULL) == 0)) return(0);
456
  if ((k == ' ') && (db->fname[0] != 0) && (savefile(db, NULL) == 0)) return(0);
446
 
457
 
447
  /* any other key = cancel operation */
458
  /* any other key = cancel operation */
448
  return(1);
459
  return(1);
449
}
460
}
450
 
461
 
451
 
462
 
452
static void ui_refresh(const struct file *db) {
463
static void ui_refresh(const struct file *db) {
453
  unsigned char x;
464
  unsigned char x;
454
  const struct line far *l;
465
  const struct line far *l;
455
  unsigned char y = db->cursorposy;
466
  unsigned char y = db->cursorposy;
456
 
467
 
457
  /* quit early if nothing to refresh */
468
  /* quit early if nothing to refresh */
458
  if (uidirty.from == 0xff) return;
469
  if (uidirty.from == 0xff) return;
459
 
470
 
460
#ifdef DBG_REFRESH
471
#ifdef DBG_REFRESH
461
  static char m = 'a';
472
  static char m = 'a';
462
  m++;
473
  m++;
463
  if (m > 'z') m = 'a';
474
  if (m > 'z') m = 'a';
464
#endif
475
#endif
465
 
476
 
466
  /* rewind cursor line to first line that needs redrawing */
477
  /* rewind cursor line to first line that needs redrawing */
467
  for (l = db->cursor; y > uidirty.from; y--) l = l->prev;
478
  for (l = db->cursor; y > uidirty.from; y--) l = l->prev;
468
 
479
 
469
  /* iterate over lines and redraw whatever needs to be redrawn */
480
  /* iterate over lines and redraw whatever needs to be redrawn */
470
  for (; l != NULL; l = l->next, y++) {
481
  for (; l != NULL; l = l->next, y++) {
471
 
482
 
472
    /* skip lines that do not need to be refreshed */
483
    /* skip lines that do not need to be refreshed */
473
    if (y < uidirty.from) continue;
484
    if (y < uidirty.from) continue;
474
    if (y > uidirty.to) break;
485
    if (y > uidirty.to) break;
475
 
486
 
476
    x = 0;
487
    x = 0;
477
    if (db->xoffset < l->len) {
488
    if (db->xoffset < l->len) {
478
      unsigned char i, limit;
489
      unsigned char i, limit;
479
      if (l->len - db->xoffset < screenw) {
490
      if (l->len - db->xoffset < screenw) {
480
        limit = l->len;
491
        limit = l->len;
481
      } else {
492
      } else {
482
        limit = db->xoffset + screenlastcol;
493
        limit = db->xoffset + screenlastcol;
483
      }
494
      }
484
      for (i = db->xoffset; i < limit; i++) mdr_cout_char(y, x++, l->payload[i], SCHEME_TEXT);
495
      for (i = db->xoffset; i < limit; i++) mdr_cout_char(y, x++, l->payload[i], SCHEME_TEXT);
485
    }
496
    }
486
 
497
 
487
    /* write empty spaces until end of line */
498
    /* write empty spaces until end of line */
488
    if (x < screenlastcol) mdr_cout_char_rep(y, x, ' ', SCHEME_TEXT, screenlastcol - x);
499
    if (x < screenlastcol) mdr_cout_char_rep(y, x, ' ', SCHEME_TEXT, screenlastcol - x);
489
 
500
 
490
#ifdef DBG_REFRESH
501
#ifdef DBG_REFRESH
491
    mdr_cout_char(y, 0, m, SCHEME_STBAR1);
502
    mdr_cout_char(y, 0, m, SCHEME_STBAR1);
492
#endif
503
#endif
493
 
504
 
494
    if (y == screenh - 2) break;
505
    if (y == screenh - 2) break;
495
  }
506
  }
496
 
507
 
497
  /* fill all lines below if empty (and they need to be redrawn) */
508
  /* fill all lines below if empty (and they need to be redrawn) */
498
  if (l == NULL) {
509
  if (l == NULL) {
499
    while ((y < screenlastrow) && (y < uidirty.to)) {
510
    while ((y < screenlastrow) && (y < uidirty.to)) {
500
      mdr_cout_char_rep(y++, 0, ' ', SCHEME_TEXT, screenlastcol);
511
      mdr_cout_char_rep(y++, 0, ' ', SCHEME_TEXT, screenlastcol);
501
    }
512
    }
502
  }
513
  }
503
 
514
 
504
  /* scroll bar */
515
  /* scroll bar */
505
  for (y = 0; y < screenlastrow; y++) {
516
  for (y = 0; y < screenlastrow; y++) {
506
    mdr_cout_char(y, screenlastcol, SCROLL_CURSOR, SCHEME_SCROLL);
517
    mdr_cout_char(y, screenlastcol, SCROLL_CURSOR, SCHEME_SCROLL);
507
  }
518
  }
508
 
519
 
509
  /* scroll cursor */
520
  /* scroll cursor */
510
  if (db->totlines >= screenh) {
521
  if (db->totlines >= screenh) {
511
    unsigned short topline = db->curline - db->cursorposy;
522
    unsigned short topline = db->curline - db->cursorposy;
512
    unsigned short col;
523
    unsigned short col;
513
    unsigned short totlines = db->totlines - screenh + 1;
524
    unsigned short totlines = db->totlines - screenh + 1;
514
    if (db->totlines - screenh > screenh) {
525
    if (db->totlines - screenh > screenh) {
515
      col = topline / (totlines / screenlastrow);
526
      col = topline / (totlines / screenlastrow);
516
    } else {
527
    } else {
517
      col = topline * screenlastrow / totlines;
528
      col = topline * screenlastrow / totlines;
518
    }
529
    }
519
    if (col >= screenlastrow) col = screenh - 2;
530
    if (col >= screenlastrow) col = screenh - 2;
520
    mdr_cout_char(col, screenlastcol, ' ', SCHEME_SCROLL);
531
    mdr_cout_char(col, screenlastcol, ' ', SCHEME_SCROLL);
521
  }
532
  }
522
 
533
 
523
  /* clear out the dirty flag */
534
  /* clear out the dirty flag */
524
  uidirty.from = 0xff;
535
  uidirty.from = 0xff;
525
}
536
}
526
 
537
 
527
 
538
 
528
static void check_cursor_not_after_eol(struct file *db) {
539
static void check_cursor_not_after_eol(struct file *db) {
529
  if (db->xoffset + db->cursorposx <= db->cursor->len) return;
540
  if (db->xoffset + db->cursorposx <= db->cursor->len) return;
530
 
541
 
531
  if (db->cursor->len < db->xoffset) {
542
  if (db->cursor->len < db->xoffset) {
532
    db->cursorposx = 0;
543
    db->cursorposx = 0;
533
    db->xoffset = db->cursor->len;
544
    db->xoffset = db->cursor->len;
534
    uidirty.from = 0;
545
    uidirty.from = 0;
535
    uidirty.to = 0xff;
546
    uidirty.to = 0xff;
536
  } else {
547
  } else {
537
    db->cursorposx = db->cursor->len - db->xoffset;
548
    db->cursorposx = db->cursor->len - db->xoffset;
538
  }
549
  }
539
}
550
}
540
 
551
 
541
 
552
 
542
static void cursor_up(struct file *db) {
553
static void cursor_up(struct file *db) {
543
  if (db->cursor->prev == NULL) return;
554
  if (db->cursor->prev == NULL) return;
544
 
555
 
545
  db->curline -= 1;
556
  db->curline -= 1;
546
  db->cursor = db->cursor->prev;
557
  db->cursor = db->cursor->prev;
547
  if (db->cursorposy == 0) {
558
  if (db->cursorposy == 0) {
548
    uidirty.from = 0;
559
    uidirty.from = 0;
549
    uidirty.to = 0xff;
560
    uidirty.to = 0xff;
550
  } else {
561
  } else {
551
    db->cursorposy -= 1;
562
    db->cursorposy -= 1;
552
  }
563
  }
553
}
564
}
554
 
565
 
555
 
566
 
556
static void cursor_eol(struct file *db) {
567
static void cursor_eol(struct file *db) {
557
  /* adjust xoffset to make sure eol is visible on screen */
568
  /* adjust xoffset to make sure eol is visible on screen */
558
  if (db->xoffset > db->cursor->len) {
569
  if (db->xoffset > db->cursor->len) {
559
    db->xoffset = db->cursor->len - 1;
570
    db->xoffset = db->cursor->len - 1;
560
    uidirty.from = 0;
571
    uidirty.from = 0;
561
    uidirty.to = 0xff;
572
    uidirty.to = 0xff;
562
  }
573
  }
563
 
574
 
564
  if (db->xoffset + screenlastcol <= db->cursor->len) {
575
  if (db->xoffset + screenlastcol <= db->cursor->len) {
565
    db->xoffset = db->cursor->len - screenw + 2;
576
    db->xoffset = db->cursor->len - screenw + 2;
566
    uidirty.from = 0;
577
    uidirty.from = 0;
567
    uidirty.to = 0xff;
578
    uidirty.to = 0xff;
568
  }
579
  }
569
  db->cursorposx = db->cursor->len - db->xoffset;
580
  db->cursorposx = db->cursor->len - db->xoffset;
570
}
581
}
571
 
582
 
572
 
583
 
573
static void cursor_down(struct file *db) {
584
static void cursor_down(struct file *db) {
574
  if (db->cursor->next == NULL) return;
585
  if (db->cursor->next == NULL) return;
575
 
586
 
576
  db->curline += 1;
587
  db->curline += 1;
577
  db->cursor = db->cursor->next;
588
  db->cursor = db->cursor->next;
578
  if (db->cursorposy < screenh - 2) {
589
  if (db->cursorposy < screenh - 2) {
579
    db->cursorposy += 1;
590
    db->cursorposy += 1;
580
  } else {
591
  } else {
581
    uidirty.from = 0;
592
    uidirty.from = 0;
582
    uidirty.to = 0xff;
593
    uidirty.to = 0xff;
583
  }
594
  }
584
}
595
}
585
 
596
 
586
 
597
 
587
static void cursor_left(struct file *db) {
598
static void cursor_left(struct file *db) {
588
  if (db->cursorposx > 0) {
599
  if (db->cursorposx > 0) {
589
    db->cursorposx -= 1;
600
    db->cursorposx -= 1;
590
  } else if (db->xoffset > 0) {
601
  } else if (db->xoffset > 0) {
591
    db->xoffset -= 1;
602
    db->xoffset -= 1;
592
    uidirty.from = 0;
603
    uidirty.from = 0;
593
    uidirty.to = 0xff;
604
    uidirty.to = 0xff;
594
  } else if (db->cursor->prev != NULL) { /* jump to end of line above */
605
  } else if (db->cursor->prev != NULL) { /* jump to end of line above */
595
    cursor_up(db);
606
    cursor_up(db);
596
    cursor_eol(db);
607
    cursor_eol(db);
597
  }
608
  }
598
}
609
}
599
 
610
 
600
 
611
 
601
static void cursor_home(struct file *db) {
612
static void cursor_home(struct file *db) {
602
  db->cursorposx = 0;
613
  db->cursorposx = 0;
603
  if (db->xoffset != 0) {
614
  if (db->xoffset != 0) {
604
    db->xoffset = 0;
615
    db->xoffset = 0;
605
    uidirty.from = 0;
616
    uidirty.from = 0;
606
    uidirty.to = 0xff;
617
    uidirty.to = 0xff;
607
  }
618
  }
608
}
619
}
609
 
620
 
610
 
621
 
611
static void cursor_right(struct file *db) {
622
static void cursor_right(struct file *db) {
612
  if (db->cursor->len > db->xoffset + db->cursorposx) {
623
  if (db->cursor->len > db->xoffset + db->cursorposx) {
613
    if (db->cursorposx < screenw - 2) {
624
    if (db->cursorposx < screenw - 2) {
614
      db->cursorposx += 1;
625
      db->cursorposx += 1;
615
    } else {
626
    } else {
616
      db->xoffset += 1;
627
      db->xoffset += 1;
617
      uidirty.from = 0;
628
      uidirty.from = 0;
618
      uidirty.to = 0xff;
629
      uidirty.to = 0xff;
619
    }
630
    }
620
  } else if (db->cursor->next != NULL) { /* jump to start of next line */
631
  } else if (db->cursor->next != NULL) { /* jump to start of next line */
621
    cursor_down(db);
632
    cursor_down(db);
622
    cursor_home(db);
633
    cursor_home(db);
623
  }
634
  }
624
}
635
}
625
 
636
 
626
 
637
 
627
static void del(struct file *db) {
638
static void del(struct file *db) {
628
  if (db->cursorposx + db->xoffset < db->cursor->len) {
639
  if (db->cursorposx + db->xoffset < db->cursor->len) {
629
    fmemmove(db->cursor->payload + db->cursorposx + db->xoffset, db->cursor->payload + db->cursorposx + db->xoffset + 1, db->cursor->len - db->cursorposx - db->xoffset);
640
    fmemmove(db->cursor->payload + db->cursorposx + db->xoffset, db->cursor->payload + db->cursorposx + db->xoffset + 1, db->cursor->len - db->cursorposx - db->xoffset);
630
    db->cursor->len -= 1; /* do this AFTER memmove so the copy includes the nul terminator */
641
    db->cursor->len -= 1; /* do this AFTER memmove so the copy includes the nul terminator */
631
    uidirty.from = db->cursorposy;
642
    uidirty.from = db->cursorposy;
632
    uidirty.to = db->cursorposy;
643
    uidirty.to = db->cursorposy;
633
    db->modflag = 1;
644
    db->modflag = 1;
634
  } else if (db->cursor->next != NULL) { /* cursor is at end of line: merge current line with next one (if there is a next one) */
645
  } else if (db->cursor->next != NULL) { /* cursor is at end of line: merge current line with next one (if there is a next one) */
635
    struct line far *nextline = db->cursor->next;
646
    struct line far *nextline = db->cursor->next;
636
    if (db->cursor->next->len > 0) {
647
    if (db->cursor->next->len > 0) {
637
      if (curline_resize(db, db->cursor->len + db->cursor->next->len + 1) == 0) {
648
      if (curline_resize(db, db->cursor->len + db->cursor->next->len + 1) == 0) {
638
        fmemmove(db->cursor->payload + db->cursor->len, db->cursor->next->payload, db->cursor->next->len + 1);
649
        fmemmove(db->cursor->payload + db->cursor->len, db->cursor->next->payload, db->cursor->next->len + 1);
639
        db->cursor->len += db->cursor->next->len;
650
        db->cursor->len += db->cursor->next->len;
640
      }
651
      }
641
    }
652
    }
642
 
653
 
643
    db->cursor->next = db->cursor->next->next;
654
    db->cursor->next = db->cursor->next->next;
644
    db->cursor->next->prev = db->cursor;
655
    db->cursor->next->prev = db->cursor;
645
 
656
 
646
    line_free(nextline);
657
    line_free(nextline);
647
    uidirty.from = db->cursorposy;
658
    uidirty.from = db->cursorposy;
648
    uidirty.to = 0xff;
659
    uidirty.to = 0xff;
649
    db->totlines -= 1;
660
    db->totlines -= 1;
650
    db->modflag = 1;
661
    db->modflag = 1;
651
  }
662
  }
652
}
663
}
653
 
664
 
654
 
665
 
655
static void bkspc(struct file *db) {
666
static void bkspc(struct file *db) {
656
  /* backspace is basically "left + del", not applicable only if cursor is on 1st byte of the file */
667
  /* backspace is basically "left + del", not applicable only if cursor is on 1st byte of the file */
657
  if ((db->cursorposx + db->xoffset == 0) && (db->cursor->prev == NULL)) return;
668
  if ((db->cursorposx + db->xoffset == 0) && (db->cursor->prev == NULL)) return;
658
 
669
 
659
  cursor_left(db);
670
  cursor_left(db);
660
  del(db);
671
  del(db);
661
}
672
}
662
 
673
 
663
 
674
 
664
#define LOADFILE_FILENOTFOUND 2
675
#define LOADFILE_FILENOTFOUND 2
665
 
676
 
666
/* returns 0 on success, 1 on file not found, 2 on other error */
677
/* returns 0 on success, 1 on file not found, 2 on other error */
667
static unsigned char loadfile(struct file *db, const char *fname) {
678
static unsigned char loadfile(struct file *db, const char *fname) {
668
  char *buffptr;
679
  char *buffptr;
669
  unsigned short len, llen;
680
  unsigned short len, llen;
670
  unsigned short fd;
681
  unsigned short fd;
671
  unsigned char eolfound;
682
  unsigned char eolfound;
672
  unsigned char err = 0;
683
  unsigned char err = 0;
673
 
684
 
674
  /* free the entire linked list of lines */
685
  /* free the entire linked list of lines */
675
  db_rewind(db);
686
  db_rewind(db);
676
  while (db->cursor) {
687
  while (db->cursor) {
677
    struct line far *victim;
688
    struct line far *victim;
678
    victim = db->cursor;
689
    victim = db->cursor;
679
    db->cursor = db->cursor->next;
690
    db->cursor = db->cursor->next;
680
    line_free(victim);
691
    line_free(victim);
681
  }
692
  }
682
 
693
 
683
  /* zero out the struct (take care to preserve the id of the slot, though) */
694
  /* zero out the struct (take care to preserve the id of the slot, though) */
684
  {
695
  {
685
    unsigned char slotid = db->slotid;
696
    unsigned char slotid = db->slotid;
686
    bzero(db, sizeof(struct file));
697
    bzero(db, sizeof(struct file));
687
    db->slotid = slotid;
698
    db->slotid = slotid;
688
  }
699
  }
689
 
700
 
690
  /* start by adding an empty line */
701
  /* start by adding an empty line */
691
  if (line_add(db, NULL, 0) != 0) return(2);
702
  if (line_add(db, NULL, 0) != 0) return(2);
692
 
703
 
693
  if (fname == NULL) goto SKIPLOADING;
704
  if (fname == NULL) goto SKIPLOADING;
694
 
705
 
695
  /* make the filename canonical (DOS 3+ only, on earlier versions it just copies the filename) */
706
  /* make the filename canonical (DOS 3+ only, on earlier versions it just copies the filename) */
696
  mdr_dos_truename(db->fname, fname);
707
  mdr_dos_truename(db->fname, fname);
697
 
708
 
698
  /* fopen file */
709
  /* fopen file */
699
  fd = 0;
710
  fd = 0;
700
  _asm {
711
  _asm {
701
    push cx
712
    push cx
702
    push dx
713
    push dx
703
 
714
 
704
    mov ax, 0x3d00
715
    mov ax, 0x3d00
705
    mov dx, fname   // works only in SMALL memory model!
716
    mov dx, fname   // works only in SMALL memory model!
706
    xor cl, cl
717
    xor cl, cl
707
    int 0x21
718
    int 0x21
708
    mov fd, ax
719
    mov fd, ax
709
    jnc done
720
    jnc done
710
    mov err, al
721
    mov err, al
711
    done:
722
    done:
712
 
723
 
713
    pop dx
724
    pop dx
714
    pop cx
725
    pop cx
715
  }
726
  }
716
 
727
 
717
  if (err != 0) goto SKIPLOADING;
728
  if (err != 0) goto SKIPLOADING;
718
 
729
 
719
  db->lfonly = 1;
730
  db->lfonly = 1;
720
 
731
 
721
  for (eolfound = 0;;) {
732
  for (eolfound = 0;;) {
722
    unsigned short consumedbytes;
733
    unsigned short consumedbytes;
723
 
734
 
724
    if ((mdr_dos_read(fd, buff, sizeof(buff), &len) != 0) || (len == 0)) break;
735
    if ((mdr_dos_read(fd, buff, sizeof(buff), &len) != 0) || (len == 0)) break;
725
    buffptr = buff;
736
    buffptr = buff;
726
 
737
 
727
    FINDLINE:
738
    FINDLINE:
728
 
739
 
729
    /* look for nearest \n (also expand tabs) */
740
    /* look for nearest \n (also expand tabs) */
730
    for (consumedbytes = 0;; consumedbytes++) {
741
    for (consumedbytes = 0;; consumedbytes++) {
731
 
742
 
732
      if (buffptr[consumedbytes] == '\t') {
743
      if (buffptr[consumedbytes] == '\t') {
733
        llen = consumedbytes;
744
        llen = consumedbytes;
734
        break;
745
        break;
735
      }
746
      }
736
 
747
 
737
      if (consumedbytes == len) {
748
      if (consumedbytes == len) {
738
        llen = consumedbytes;
749
        llen = consumedbytes;
739
        break;
750
        break;
740
      }
751
      }
741
      if (buffptr[consumedbytes] == '\r') {
752
      if (buffptr[consumedbytes] == '\r') {
742
        llen = consumedbytes;
753
        llen = consumedbytes;
743
        consumedbytes++;
754
        consumedbytes++;
744
        db->lfonly = 0;
755
        db->lfonly = 0;
745
        break;
756
        break;
746
      }
757
      }
747
      if (buffptr[consumedbytes] == '\n') {
758
      if (buffptr[consumedbytes] == '\n') {
748
        eolfound = 1;
759
        eolfound = 1;
749
        llen = consumedbytes;
760
        llen = consumedbytes;
750
        consumedbytes++;
761
        consumedbytes++;
751
        break;
762
        break;
752
      }
763
      }
753
    }
764
    }
754
 
765
 
755
    /* consumedbytes is the amount of bytes processed from buffptr,
766
    /* consumedbytes is the amount of bytes processed from buffptr,
756
     * llen is the length of line's payload (without its line terminator) */
767
     * llen is the length of line's payload (without its line terminator) */
757
 
768
 
758
    /* append content, if line is non-empty */
769
    /* append content, if line is non-empty */
759
    if ((llen > 0) && (line_append(db, buffptr, llen) != 0)) {
770
    if ((llen > 0) && (line_append(db, buffptr, llen) != 0)) {
760
      goto IOERR;
771
      goto IOERR;
761
    }
772
    }
762
 
773
 
763
    /* allocate the next line if current line ended */
774
    /* allocate the next line if current line ended */
764
    if (eolfound) {
775
    if (eolfound) {
765
      if (line_add(db, NULL, 0) != 0) {
776
      if (line_add(db, NULL, 0) != 0) {
766
        goto IOERR;
777
        goto IOERR;
767
      }
778
      }
768
      eolfound = 0;
779
      eolfound = 0;
769
    }
780
    }
770
 
781
 
771
    /* append 8 spaces if tab char found */
782
    /* append 8 spaces if tab char found */
772
    if ((consumedbytes < len) && (buffptr[consumedbytes] == '\t') && (glob_tablessmode == 0)) {
783
    if ((consumedbytes < len) && (buffptr[consumedbytes] == '\t') && (glob_tablessmode == 0)) {
773
      consumedbytes++;
784
      consumedbytes++;
774
      if (line_append(db, "        ", 8) != 0) {
785
      if (line_append(db, "        ", 8) != 0) {
775
        goto IOERR;
786
        goto IOERR;
776
      }
787
      }
777
    }
788
    }
778
 
789
 
779
    /* anything left? process the buffer leftover again */
790
    /* anything left? process the buffer leftover again */
780
    if (consumedbytes < len) {
791
    if (consumedbytes < len) {
781
      len -= consumedbytes;
792
      len -= consumedbytes;
782
      buffptr += consumedbytes;
793
      buffptr += consumedbytes;
783
      goto FINDLINE;
794
      goto FINDLINE;
784
    }
795
    }
785
 
796
 
786
  }
797
  }
787
 
798
 
788
  mdr_dos_fclose(fd);
799
  dosfclose(fd);
789
 
800
 
790
  SKIPLOADING:
801
  SKIPLOADING:
791
 
802
 
792
  /* rewind cursor to top of file because it has been used by line_add() */
803
  /* rewind cursor to top of file because it has been used by line_add() */
793
  db_rewind(db);
804
  db_rewind(db);
794
 
805
 
795
  return(err);
806
  return(err);
796
 
807
 
797
  IOERR:
808
  IOERR:
798
  mdr_dos_fclose(fd);
809
  dosfclose(fd);
799
  return(1);
810
  return(1);
800
}
811
}
801
 
812
 
802
 
813
 
803
/* a custom argv-parsing routine that looks directly inside the PSP, avoids the need
814
/* a custom argv-parsing routine that looks directly inside the PSP, avoids the need
804
 * of argc and argv, saves some 330 bytes of binary size
815
 * of argc and argv, saves some 330 bytes of binary size
805
 * returns non-zero on error */
816
 * returns non-zero on error */
806
static int parseargv(struct file *dbarr) {
817
static int parseargv(struct file *dbarr) {
807
  char *tail = (void *)0x81; /* THIS WORKS ONLY IN SMALL MEMORY MODEL */
818
  char *tail = (void *)0x81; /* THIS WORKS ONLY IN SMALL MEMORY MODEL */
808
  unsigned short count = 0;
819
  unsigned short count = 0;
809
  char *arg;
820
  char *arg;
810
  unsigned short lastarg = 0;
821
  unsigned short lastarg = 0;
811
  unsigned char err;
822
  unsigned char err;
812
 
823
 
813
  while (!lastarg) {
824
  while (!lastarg) {
814
    /* jump to nearest arg */
825
    /* jump to nearest arg */
815
    while (*tail == ' ') {
826
    while (*tail == ' ') {
816
      *tail = 0;
827
      *tail = 0;
817
      tail++;
828
      tail++;
818
    }
829
    }
819
 
830
 
820
    if (*tail == '\r') {
831
    if (*tail == '\r') {
821
      *tail = 0;
832
      *tail = 0;
822
      break;
833
      break;
823
    }
834
    }
824
 
835
 
825
    arg = tail;
836
    arg = tail;
826
 
837
 
827
    /* jump to next delimiter */
838
    /* jump to next delimiter */
828
    while ((*tail != ' ') && (*tail != '\r')) tail++;
839
    while ((*tail != ' ') && (*tail != '\r')) tail++;
829
 
840
 
830
    /* if \r then remember this is the last arg */
841
    /* if \r then remember this is the last arg */
831
    if (*tail == '\r') lastarg = 1;
842
    if (*tail == '\r') lastarg = 1;
832
 
843
 
833
    *tail = 0;
844
    *tail = 0;
834
    tail++;
845
    tail++;
835
 
846
 
836
    /* look at the arg now */
847
    /* look at the arg now */
837
    if (*arg == '/') {
848
    if (*arg == '/') {
838
      if (arg[1] == 't') { /* /t = do not expand tabs */
849
      if (arg[1] == 't') { /* /t = do not expand tabs */
839
        glob_tablessmode = 1;
850
        glob_tablessmode = 1;
840
 
851
 
841
      } else if (arg[1] == 'm') { /* /m = force mono mode */
852
      } else if (arg[1] == 'm') { /* /m = force mono mode */
842
        glob_monomode = 1;
853
        glob_monomode = 1;
843
 
854
 
844
      } else {  /* help screen */
855
      } else {  /* help screen */
845
        mdr_coutraw_str(svarlang_str(1,3)); /* Sved, the SvarDOS editor */
856
        mdr_coutraw_str(svarlang_str(1,3)); /* Sved, the SvarDOS editor */
846
        mdr_coutraw_str(" [");
857
        mdr_coutraw_str(" [");
847
        mdr_coutraw_str(svarlang_str(1,4)); /* ver */
858
        mdr_coutraw_str(svarlang_str(1,4)); /* ver */
848
        mdr_coutraw_puts(" " PVER "]");
859
        mdr_coutraw_puts(" " PVER "]");
849
        mdr_coutraw_puts("Copyright (C) " PDATE " Mateusz Viste");
860
        mdr_coutraw_puts("Copyright (C) " PDATE " Mateusz Viste");
850
        mdr_coutraw_crlf();
861
        mdr_coutraw_crlf();
851
        mdr_coutraw_str("sved [/m] [/t] ");
862
        mdr_coutraw_str("sved [/m] [/t] ");
852
        mdr_coutraw_puts(svarlang_str(1,1)); /* args syntax */
863
        mdr_coutraw_puts(svarlang_str(1,1)); /* args syntax */
853
        mdr_coutraw_crlf();
864
        mdr_coutraw_crlf();
854
        mdr_coutraw_puts(svarlang_str(1,10)); /* /m */
865
        mdr_coutraw_puts(svarlang_str(1,10)); /* /m */
855
        mdr_coutraw_puts(svarlang_str(1,11)); /* /t */
866
        mdr_coutraw_puts(svarlang_str(1,11)); /* /t */
856
        return(-1);
867
        return(-1);
857
      }
868
      }
858
      continue;
869
      continue;
859
    }
870
    }
860
 
871
 
861
    /* looks to be a filename */
872
    /* looks to be a filename */
862
    if (count == 10) {
873
    if (count == 10) {
863
      mdr_coutraw_puts(svarlang_str(0,12)); /* too many files */
874
      mdr_coutraw_puts(svarlang_str(0,12)); /* too many files */
864
      return(-1);
875
      return(-1);
865
    }
876
    }
866
 
877
 
867
    /* try loading it */
878
    /* try loading it */
868
    mdr_coutraw_str(svarlang_str(1,2));
879
    mdr_coutraw_str(svarlang_str(1,2));
869
    mdr_coutraw_char(' ');
880
    mdr_coutraw_char(' ');
870
    mdr_coutraw_puts(arg);
881
    mdr_coutraw_puts(arg);
871
    err = loadfile(&(dbarr[count]), arg);
882
    err = loadfile(&(dbarr[count]), arg);
872
    if (err) {
883
    if (err) {
873
      if (err == LOADFILE_FILENOTFOUND) { /* file not found */
884
      if (err == LOADFILE_FILENOTFOUND) { /* file not found */
874
        if ((count == 0) && (lastarg != 0)) { /* a 'file not found' is fine if only one file was given */
885
        if ((count == 0) && (lastarg != 0)) { /* a 'file not found' is fine if only one file was given */
875
          err = 0;
886
          err = 0;
876
        } else {
887
        } else {
877
          err = 11;
888
          err = 11;
878
        }
889
        }
879
      } else { /* general error */
890
      } else { /* general error */
880
        err = 10;
891
        err = 10;
881
      }
892
      }
882
      if (err) {
893
      if (err) {
883
        mdr_coutraw_puts(svarlang_str(0,err));
894
        mdr_coutraw_puts(svarlang_str(0,err));
884
        return(-1);
895
        return(-1);
885
      }
896
      }
886
    }
897
    }
887
    count++;
898
    count++;
888
  }
899
  }
889
 
900
 
890
  return(0);
901
  return(0);
891
}
902
}
892
 
903
 
893
 
904
 
894
static void insert_in_line(struct file *db, const char *databuf, unsigned short len) {
905
static void insert_in_line(struct file *db, const char *databuf, unsigned short len) {
895
  if (curline_resize(db, db->cursor->len + len) == 0) {
906
  if (curline_resize(db, db->cursor->len + len) == 0) {
896
    unsigned short off = db->xoffset + db->cursorposx;
907
    unsigned short off = db->xoffset + db->cursorposx;
897
    db->modflag = 1;
908
    db->modflag = 1;
898
    fmemmove(db->cursor->payload + off + len, db->cursor->payload + off, db->cursor->len - off + 1);
909
    fmemmove(db->cursor->payload + off + len, db->cursor->payload + off, db->cursor->len - off + 1);
899
    db->cursor->len += len;
910
    db->cursor->len += len;
900
    uidirty.from = db->cursorposy;
911
    uidirty.from = db->cursorposy;
901
    uidirty.to = db->cursorposy;
912
    uidirty.to = db->cursorposy;
902
    while (len--) {
913
    while (len--) {
903
      db->cursor->payload[off++] = *databuf;
914
      db->cursor->payload[off++] = *databuf;
904
      databuf++;
915
      databuf++;
905
      cursor_right(db);
916
      cursor_right(db);
906
    }
917
    }
907
  }
918
  }
908
}
919
}
909
 
920
 
910
 
921
 
911
/* recompute db->curline by counting nodes in linked list */
922
/* recompute db->curline by counting nodes in linked list */
912
static void recompute_curline(struct file *db) {
923
static void recompute_curline(struct file *db) {
913
  const struct line far *l = db->cursor;
924
  const struct line far *l = db->cursor;
914
 
925
 
915
  db->curline = 0;
926
  db->curline = 0;
916
  while (l->prev != NULL) {
927
  while (l->prev != NULL) {
917
    db->curline += 1;
928
    db->curline += 1;
918
    l = l->prev;
929
    l = l->prev;
919
  }
930
  }
920
}
931
}
921
 
932
 
922
 
933
 
923
enum MENU_ACTION {
934
enum MENU_ACTION {
924
  MENU_NONE   = 0,
935
  MENU_NONE   = 0,
925
  MENU_OPEN   = 1,
936
  MENU_OPEN   = 1,
926
  MENU_SAVE   = 2,
937
  MENU_SAVE   = 2,
927
  MENU_SAVEAS = 3,
938
  MENU_SAVEAS = 3,
928
  MENU_CLOSE  = 4,
939
  MENU_CLOSE  = 4,
929
  MENU_CHGEOL = 5,
940
  MENU_CHGEOL = 5,
930
  MENU_QUIT   = 6
941
  MENU_QUIT   = 6
931
};
942
};
932
 
943
 
933
static enum MENU_ACTION ui_menu(void) {
944
static enum MENU_ACTION ui_menu(void) {
934
  unsigned short i, curchoice, attr, x, slen;
945
  unsigned short i, curchoice, attr, x, slen;
935
  unsigned short xorigin, yorigin;
946
  unsigned short xorigin, yorigin;
936
 
947
 
937
  /* find out the longest string */
948
  /* find out the longest string */
938
  slen = 0;
949
  slen = 0;
939
  for (i = MENU_OPEN; i <= MENU_QUIT; i++) {
950
  for (i = MENU_OPEN; i <= MENU_QUIT; i++) {
940
    x = strlen(svarlang_str(8, i));
951
    x = strlen(svarlang_str(8, i));
941
    if (x > slen) slen = x;
952
    if (x > slen) slen = x;
942
  }
953
  }
943
 
954
 
944
  /* calculate where to draw the menu on screen */
955
  /* calculate where to draw the menu on screen */
945
  xorigin = (screenw - (slen + 5)) / 2;
956
  xorigin = (screenw - (slen + 5)) / 2;
946
  yorigin = (screenh - (MENU_QUIT - MENU_OPEN + 6)) / 2;
957
  yorigin = (screenh - (MENU_QUIT - MENU_OPEN + 6)) / 2;
947
 
958
 
948
  /* */
959
  /* */
949
  uidirty.from = yorigin;
960
  uidirty.from = yorigin;
950
  uidirty.to = 0xff;
961
  uidirty.to = 0xff;
951
  uidirty.statusbar = 1;
962
  uidirty.statusbar = 1;
952
 
963
 
953
  /* hide the cursor */
964
  /* hide the cursor */
954
  mdr_cout_cursor_hide();
965
  mdr_cout_cursor_hide();
955
 
966
 
956
  curchoice = MENU_OPEN;
967
  curchoice = MENU_OPEN;
957
  for (;;) {
968
  for (;;) {
958
    /* render menu */
969
    /* render menu */
959
    for (i = MENU_NONE; i <= MENU_QUIT + 1; i++) {
970
    for (i = MENU_NONE; i <= MENU_QUIT + 1; i++) {
960
      mdr_cout_char_rep(yorigin + i, xorigin, ' ', SCHEME_MENU, slen+4);
971
      mdr_cout_char_rep(yorigin + i, xorigin, ' ', SCHEME_MENU, slen+4);
961
      if (i == curchoice) {
972
      if (i == curchoice) {
962
        attr = SCHEME_MENU_CUR;
973
        attr = SCHEME_MENU_CUR;
963
        mdr_cout_char_rep(yorigin + i, xorigin + 1, ' ', SCHEME_MENU_SEL, slen + 2);
974
        mdr_cout_char_rep(yorigin + i, xorigin + 1, ' ', SCHEME_MENU_SEL, slen + 2);
964
      } else {
975
      } else {
965
        attr = SCHEME_MENU;
976
        attr = SCHEME_MENU;
966
      }
977
      }
967
      mdr_cout_str(yorigin + i, xorigin + 2, svarlang_str(8, i), attr, slen);
978
      mdr_cout_str(yorigin + i, xorigin + 2, svarlang_str(8, i), attr, slen);
968
    }
979
    }
969
    /* wait for key */
980
    /* wait for key */
970
    switch (mdr_dos_getkey2()) {
981
    switch (mdr_dos_getkey2()) {
971
      case 0x150: /* down */
982
      case 0x150: /* down */
972
        if (curchoice == MENU_QUIT) {
983
        if (curchoice == MENU_QUIT) {
973
          curchoice = MENU_OPEN;
984
          curchoice = MENU_OPEN;
974
        } else {
985
        } else {
975
          curchoice++;
986
          curchoice++;
976
        }
987
        }
977
        break;
988
        break;
978
      case 0x148: /* up */
989
      case 0x148: /* up */
979
        if (curchoice == MENU_OPEN) {
990
        if (curchoice == MENU_OPEN) {
980
          curchoice = MENU_QUIT;
991
          curchoice = MENU_QUIT;
981
        } else {
992
        } else {
982
          curchoice--;
993
          curchoice--;
983
        }
994
        }
984
        break;
995
        break;
985
      default:
996
      default:
986
        curchoice = MENU_NONE;
997
        curchoice = MENU_NONE;
987
        /* FALLTHRU */
998
        /* FALLTHRU */
988
      case '\r': /* ENTER */
999
      case '\r': /* ENTER */
989
        mdr_cout_cursor_show();
1000
        mdr_cout_cursor_show();
990
        return(curchoice);
1001
        return(curchoice);
991
    }
1002
    }
992
  }
1003
  }
993
}
1004
}
994
 
1005
 
995
 
1006
 
996
static struct file *select_slot(struct file *dbarr, unsigned short curfile) {
1007
static struct file *select_slot(struct file *dbarr, unsigned short curfile) {
997
  uidirty.from = 0;
1008
  uidirty.from = 0;
998
  uidirty.to = 0xff;
1009
  uidirty.to = 0xff;
999
  uidirty.statusbar = 1;
1010
  uidirty.statusbar = 1;
1000
 
1011
 
1001
  dbarr = &(dbarr[curfile]);
1012
  dbarr = &(dbarr[curfile]);
1002
 
1013
 
1003
  /* force redraw now, because the main() routine might not if this is exit
1014
  /* force redraw now, because the main() routine might not if this is exit
1004
   * time and we want to show the user which file has unsaved changes */
1015
   * time and we want to show the user which file has unsaved changes */
1005
  ui_statusbar(dbarr);
1016
  ui_statusbar(dbarr);
1006
  ui_refresh(dbarr);
1017
  ui_refresh(dbarr);
1007
  return(dbarr);
1018
  return(dbarr);
1008
}
1019
}
1009
 
1020
 
1010
 
1021
 
1011
/* main returns nothing, ie. sved always exits with a zero exit code
1022
/* main returns nothing, ie. sved always exits with a zero exit code
1012
 * (this saves 20 bytes of executable footprint) */
1023
 * (this saves 20 bytes of executable footprint) */
1013
void main(void) {
1024
void main(void) {
1014
  static struct file dbarr[10];
1025
  static struct file dbarr[10];
1015
  struct file *db = dbarr; /* visible file is the first slot by default */
1026
  struct file *db = dbarr; /* visible file is the first slot by default */
1016
  static struct line far *clipboard;
1027
  static struct line far *clipboard;
1017
  static unsigned char original_breakflag;
1028
  static unsigned char original_breakflag;
1018
 
1029
 
1019
  { /* load NLS resource */
1030
  { /* load NLS resource */
1020
    unsigned short i = 0;
1031
    unsigned short i = 0;
1021
    const char far *selfptr;
1032
    const char far *selfptr;
1022
    char lang[8];
1033
    char lang[8];
1023
    selfptr = mdr_dos_selfexe();
1034
    selfptr = mdr_dos_selfexe();
1024
    if (selfptr != NULL) {
1035
    if (selfptr != NULL) {
1025
      do {
1036
      do {
1026
        buff[i] = selfptr[i];
1037
        buff[i] = selfptr[i];
1027
      } while (buff[i++] != 0);
1038
      } while (buff[i++] != 0);
1028
      svarlang_autoload_exepath(buff, mdr_dos_getenv(lang, "LANG", sizeof(lang)));
1039
      svarlang_autoload_exepath(buff, mdr_dos_getenv(lang, "LANG", sizeof(lang)));
1029
    }
1040
    }
1030
  }
1041
  }
1031
 
1042
 
1032
  /* preload all slots with empty files */
1043
  /* preload all slots with empty files */
1033
  {
1044
  {
1034
    unsigned short i;
1045
    unsigned short i;
1035
    for (i = 0; i < 10; i++) {
1046
    for (i = 0; i < 10; i++) {
1036
      loadfile(&(dbarr[i]), NULL);
1047
      loadfile(&(dbarr[i]), NULL);
1037
      dbarr[i].slotid = i;
1048
      dbarr[i].slotid = i;
1038
    }
1049
    }
1039
  }
1050
  }
1040
 
1051
 
1041
  /* parse argv (and load files, if any passed on) */
1052
  /* parse argv (and load files, if any passed on) */
1042
  if (parseargv(dbarr) != 0) return;
1053
  if (parseargv(dbarr) != 0) return;
1043
 
1054
 
1044
  if ((mdr_cout_init(&screenw, &screenh) != 0) && (glob_monomode == 0)) {
1055
  if ((mdr_cout_init(&screenw, &screenh) != 0) && (glob_monomode == 0)) {
1045
    /* load color scheme if mdr_cout_init returns a color flag */
1056
    /* load color scheme if mdr_cout_init returns a color flag */
1046
    SCHEME_TEXT = 0x17;
1057
    SCHEME_TEXT = 0x17;
1047
    SCHEME_MENU = 0x70;
1058
    SCHEME_MENU = 0x70;
1048
    SCHEME_MENU_CUR = 0x6f;
1059
    SCHEME_MENU_CUR = 0x6f;
1049
    SCHEME_MENU_SEL = 0x66;
1060
    SCHEME_MENU_SEL = 0x66;
1050
    SCHEME_STBAR1 = 0x70;
1061
    SCHEME_STBAR1 = 0x70;
1051
    SCHEME_STBAR2 = 0x78;
1062
    SCHEME_STBAR2 = 0x78;
1052
    SCHEME_STBAR3 = 0x3f;
1063
    SCHEME_STBAR3 = 0x3f;
1053
    SCHEME_SCROLL = 0x70;
1064
    SCHEME_SCROLL = 0x70;
1054
    SCHEME_MSG = 0x6f;
1065
    SCHEME_MSG = 0x6f;
1055
    SCHEME_ERR = 0x4f;
1066
    SCHEME_ERR = 0x4f;
1056
  }
1067
  }
1057
  screenlastrow = screenh - 1;
1068
  screenlastrow = screenh - 1;
1058
  screenlastcol = screenw - 1;
1069
  screenlastcol = screenw - 1;
1059
 
1070
 
1060
  /* instruct DOS to stop detecting CTRL+C because user needs it for
1071
  /* instruct DOS to stop detecting CTRL+C because user needs it for
1061
   * copy/paste operations. also remember the original status of the BREAK
1072
   * copy/paste operations. also remember the original status of the BREAK
1062
   * flag so I can restore it as it was before quitting. */
1073
   * flag so I can restore it as it was before quitting. */
1063
  original_breakflag = mdr_dos_ctrlc_disable();
1074
  original_breakflag = mdr_dos_ctrlc_disable();
1064
 
1075
 
1065
  for (;;) {
1076
  for (;;) {
1066
    int k;
1077
    int k;
1067
 
1078
 
1068
    /* add an extra empty line if cursor is on last line and this line is not empty */
1079
    /* add an extra empty line if cursor is on last line and this line is not empty */
1069
    if ((db->cursor->next == NULL) && (db->cursor->len != 0)) {
1080
    if ((db->cursor->next == NULL) && (db->cursor->len != 0)) {
1070
      if (line_add(db, NULL, 0) == 0) {
1081
      if (line_add(db, NULL, 0) == 0) {
1071
        db->cursor = db->cursor->prev; /* line_add() changes the cursor pointer */
1082
        db->cursor = db->cursor->prev; /* line_add() changes the cursor pointer */
1072
        db->curline -= 1;
1083
        db->curline -= 1;
1073
      }
1084
      }
1074
    }
1085
    }
1075
 
1086
 
1076
    check_cursor_not_after_eol(db);
1087
    check_cursor_not_after_eol(db);
1077
    mdr_cout_locate(db->cursorposy, db->cursorposx);
1088
    mdr_cout_locate(db->cursorposy, db->cursorposx);
1078
 
1089
 
1079
    ui_refresh(db);
1090
    ui_refresh(db);
1080
 
1091
 
1081
    if ((uidirty.statusbar != 0) || (db->modflagprev != db->modflag) || (db->curline_prev != db->curline)) {
1092
    if ((uidirty.statusbar != 0) || (db->modflagprev != db->modflag) || (db->curline_prev != db->curline)) {
1082
      ui_statusbar(db);
1093
      ui_statusbar(db);
1083
      uidirty.statusbar = 0;
1094
      uidirty.statusbar = 0;
1084
      db->modflagprev = db->modflag;
1095
      db->modflagprev = db->modflag;
1085
      db->curline_prev = db->curline;
1096
      db->curline_prev = db->curline;
1086
    }
1097
    }
1087
#ifdef DBG_LINENUM
1098
#ifdef DBG_LINENUM
1088
      {
1099
      {
1089
        char ddd[10];
1100
        char ddd[10];
1090
        db->curline += 1;
1101
        db->curline += 1;
1091
        ddd[0] = '0' + db->curline / 100;
1102
        ddd[0] = '0' + db->curline / 100;
1092
        ddd[1] = '0' + (db->curline % 100) / 10;
1103
        ddd[1] = '0' + (db->curline % 100) / 10;
1093
        ddd[2] = '0' + (db->curline % 10);
1104
        ddd[2] = '0' + (db->curline % 10);
1094
        db->curline -= 1;
1105
        db->curline -= 1;
1095
        ddd[3] = '/';
1106
        ddd[3] = '/';
1096
        ddd[4] = '0' + db->totlines / 100;
1107
        ddd[4] = '0' + db->totlines / 100;
1097
        ddd[5] = '0' + (db->totlines % 100) / 10;
1108
        ddd[5] = '0' + (db->totlines % 100) / 10;
1098
        ddd[6] = '0' + (db->totlines % 10);
1109
        ddd[6] = '0' + (db->totlines % 10);
1099
        ddd[7] = 0;
1110
        ddd[7] = 0;
1100
        mdr_cout_str(screenh - 1, 40, ddd, SCHEME_STBAR1, sizeof(ddd));
1111
        mdr_cout_str(screenh - 1, 40, ddd, SCHEME_STBAR1, sizeof(ddd));
1101
      }
1112
      }
1102
#endif
1113
#endif
1103
 
1114
 
1104
    k = mdr_dos_getkey2();
1115
    k = mdr_dos_getkey2();
1105
 
1116
 
1106
    if (k == 0x150) { /* down */
1117
    if (k == 0x150) { /* down */
1107
      cursor_down(db);
1118
      cursor_down(db);
1108
 
1119
 
1109
    } else if (k == 0x148) { /* up */
1120
    } else if (k == 0x148) { /* up */
1110
      cursor_up(db);
1121
      cursor_up(db);
1111
 
1122
 
1112
    } else if (k == 0x14D) { /* right */
1123
    } else if (k == 0x14D) { /* right */
1113
      cursor_right(db);
1124
      cursor_right(db);
1114
 
1125
 
1115
    } else if (k == 0x14B) { /* left */
1126
    } else if (k == 0x14B) { /* left */
1116
      cursor_left(db);
1127
      cursor_left(db);
1117
 
1128
 
1118
    } else if (k == 0x149) { /* pgup */
1129
    } else if (k == 0x149) { /* pgup */
1119
      unsigned char dist = db->cursorposy + screenh - 1;
1130
      unsigned char dist = db->cursorposy + screenh - 1;
1120
      while ((dist != 0) && (db->cursor->prev != NULL)) {
1131
      while ((dist != 0) && (db->cursor->prev != NULL)) {
1121
        db->cursor = db->cursor->prev;
1132
        db->cursor = db->cursor->prev;
1122
        dist--;
1133
        dist--;
1123
      }
1134
      }
1124
      if (dist != 0) {
1135
      if (dist != 0) {
1125
        db->cursorposy = 0;
1136
        db->cursorposy = 0;
1126
        db->cursorposx = 0;
1137
        db->cursorposx = 0;
1127
      } else {
1138
      } else {
1128
        dist = db->cursorposy;
1139
        dist = db->cursorposy;
1129
        while ((dist--) && (db->cursor->next)) db->cursor = db->cursor->next;
1140
        while ((dist--) && (db->cursor->next)) db->cursor = db->cursor->next;
1130
      }
1141
      }
1131
      uidirty.from = 0;
1142
      uidirty.from = 0;
1132
      uidirty.to = 0xff;
1143
      uidirty.to = 0xff;
1133
      recompute_curline(db);
1144
      recompute_curline(db);
1134
 
1145
 
1135
    } else if (k == 0x151) { /* pgdown */
1146
    } else if (k == 0x151) { /* pgdown */
1136
      unsigned char dist = screenh + screenh - db->cursorposy - 3;
1147
      unsigned char dist = screenh + screenh - db->cursorposy - 3;
1137
      while ((dist != 0) && (db->cursor->next != NULL)) {
1148
      while ((dist != 0) && (db->cursor->next != NULL)) {
1138
        db->cursor = db->cursor->next;
1149
        db->cursor = db->cursor->next;
1139
        dist--;
1150
        dist--;
1140
      }
1151
      }
1141
      if (dist != 0) {
1152
      if (dist != 0) {
1142
        db->cursorposy = screenh - 2;
1153
        db->cursorposy = screenh - 2;
1143
        if (db->totlines <= db->cursorposy) db->cursorposy = db->totlines - 1;
1154
        if (db->totlines <= db->cursorposy) db->cursorposy = db->totlines - 1;
1144
        db->cursorposx = 0;
1155
        db->cursorposx = 0;
1145
      } else {
1156
      } else {
1146
        dist = screenh - 2 - db->cursorposy;
1157
        dist = screenh - 2 - db->cursorposy;
1147
        while ((dist--) && (db->cursor->prev)) db->cursor = db->cursor->prev;
1158
        while ((dist--) && (db->cursor->prev)) db->cursor = db->cursor->prev;
1148
      }
1159
      }
1149
      uidirty.from = 0;
1160
      uidirty.from = 0;
1150
      uidirty.to = 0xff;
1161
      uidirty.to = 0xff;
1151
      recompute_curline(db);
1162
      recompute_curline(db);
1152
 
1163
 
1153
    } else if (k == 0x147) { /* home */
1164
    } else if (k == 0x147) { /* home */
1154
       cursor_home(db);
1165
       cursor_home(db);
1155
 
1166
 
1156
    } else if (k == 0x14F) { /* end */
1167
    } else if (k == 0x14F) { /* end */
1157
       cursor_eol(db);
1168
       cursor_eol(db);
1158
 
1169
 
1159
    } else if (k == 0x1B) { /* ESC */
1170
    } else if (k == 0x1B) { /* ESC */
1160
      int quitnow = 0;
1171
      int quitnow = 0;
1161
      char fname[64];
1172
      char fname[64];
1162
      int saveflag = 0;
1173
      int saveflag = 0;
1163
      enum MENU_ACTION ui_action;
1174
      enum MENU_ACTION ui_action;
1164
 
1175
 
1165
      /* collect the exact menu action and clear the screen */
1176
      /* collect the exact menu action and clear the screen */
1166
      ui_action = ui_menu();
1177
      ui_action = ui_menu();
1167
      ui_refresh(db);
1178
      ui_refresh(db);
1168
 
1179
 
1169
      switch (ui_action) {
1180
      switch (ui_action) {
1170
 
1181
 
1171
        case MENU_NONE:
1182
        case MENU_NONE:
1172
          break;
1183
          break;
1173
 
1184
 
1174
        case MENU_OPEN:
1185
        case MENU_OPEN:
1175
          /* display a warning if unsaved changes are pending */
1186
          /* display a warning if unsaved changes are pending */
1176
          if (db->modflag != 0) ui_msg(4, 8, 0, SCHEME_MSG);
1187
          if (db->modflag != 0) ui_msg(4, 8, 0, SCHEME_MSG);
1177
 
1188
 
1178
          /* ask for filename */
1189
          /* ask for filename */
1179
          ui_getstring(svarlang_str(0,7), fname, sizeof(fname));
1190
          ui_getstring(svarlang_str(0,7), fname, sizeof(fname));
1180
          if (fname[0] != 0) {
1191
          if (fname[0] != 0) {
1181
            unsigned char err;
1192
            unsigned char err;
1182
            err = loadfile(db, fname);
1193
            err = loadfile(db, fname);
1183
            if (err != 0) {
1194
            if (err != 0) {
1184
              if (err == LOADFILE_FILENOTFOUND) {
1195
              if (err == LOADFILE_FILENOTFOUND) {
1185
                ui_msg(11, 0, 0, SCHEME_ERR); /* file not found */
1196
                ui_msg(11, 0, 0, SCHEME_ERR); /* file not found */
1186
              } else {
1197
              } else {
1187
                ui_msg(10, 0, 0, SCHEME_ERR);  /* ERROR */
1198
                ui_msg(10, 0, 0, SCHEME_ERR);  /* ERROR */
1188
              }
1199
              }
1189
              mdr_bios_tickswait(44); /* 3s */
1200
              mdr_bios_tickswait(44); /* 3s */
1190
              loadfile(db, NULL);
1201
              loadfile(db, NULL);
1191
            }
1202
            }
1192
          }
1203
          }
1193
          uidirty.from = 0;
1204
          uidirty.from = 0;
1194
          uidirty.to = 0xff;
1205
          uidirty.to = 0xff;
1195
          uidirty.statusbar = 1;
1206
          uidirty.statusbar = 1;
1196
          break;
1207
          break;
1197
 
1208
 
1198
        case MENU_SAVEAS:
1209
        case MENU_SAVEAS:
1199
          saveflag = 1;
1210
          saveflag = 1;
1200
          /* FALLTHRU */
1211
          /* FALLTHRU */
1201
        case MENU_SAVE:
1212
        case MENU_SAVE:
1202
          if ((saveflag != 0) || (db->fname[0] == 0)) { /* save as... */
1213
          if ((saveflag != 0) || (db->fname[0] == 0)) { /* save as... */
1203
            ui_getstring(svarlang_str(0,6), fname, sizeof(fname));
1214
            ui_getstring(svarlang_str(0,6), fname, sizeof(fname));
1204
            if (*fname == 0) break;
1215
            if (*fname == 0) break;
1205
            saveflag = savefile(db, fname);
1216
            saveflag = savefile(db, fname);
1206
          } else {
1217
          } else {
1207
            saveflag = savefile(db, NULL);
1218
            saveflag = savefile(db, NULL);
1208
          }
1219
          }
1209
 
1220
 
1210
          mdr_cout_cursor_hide();
1221
          mdr_cout_cursor_hide();
1211
 
1222
 
1212
          if (saveflag == 0) {
1223
          if (saveflag == 0) {
1213
            ui_msg(2, 0, 0, SCHEME_MSG);
1224
            ui_msg(2, 0, 0, SCHEME_MSG);
1214
            mdr_bios_tickswait(11); /* 11 ticks is about 600 ms */
1225
            mdr_bios_tickswait(11); /* 11 ticks is about 600 ms */
1215
          } else {
1226
          } else {
1216
            ui_msg(10, 0, 0, SCHEME_ERR);
1227
            ui_msg(10, 0, 0, SCHEME_ERR);
1217
            mdr_bios_tickswait(36); /* 2s */
1228
            mdr_bios_tickswait(36); /* 2s */
1218
          }
1229
          }
1219
          mdr_cout_cursor_show();
1230
          mdr_cout_cursor_show();
1220
          break;
1231
          break;
1221
 
1232
 
1222
        case MENU_CLOSE:
1233
        case MENU_CLOSE:
1223
          if (ui_confirm_if_unsaved(db) == 0) {
1234
          if (ui_confirm_if_unsaved(db) == 0) {
1224
            loadfile(db, NULL);
1235
            loadfile(db, NULL);
1225
          }
1236
          }
1226
          uidirty.from = 0;
1237
          uidirty.from = 0;
1227
          uidirty.to = 0xff;
1238
          uidirty.to = 0xff;
1228
          uidirty.statusbar = 1;
1239
          uidirty.statusbar = 1;
1229
          break;
1240
          break;
1230
 
1241
 
1231
        case MENU_CHGEOL:
1242
        case MENU_CHGEOL:
1232
          db->modflag = 1;
1243
          db->modflag = 1;
1233
          db->lfonly ^= 1;
1244
          db->lfonly ^= 1;
1234
          break;
1245
          break;
1235
 
1246
 
1236
        case MENU_QUIT:
1247
        case MENU_QUIT:
1237
          quitnow = 1;
1248
          quitnow = 1;
1238
          {
1249
          {
1239
            unsigned short curfile;
1250
            unsigned short curfile;
1240
            for (curfile = 0; curfile < 10; curfile++) {
1251
            for (curfile = 0; curfile < 10; curfile++) {
1241
              if (dbarr[curfile].modflag == 0) continue; /* skip unmodified slots */
1252
              if (dbarr[curfile].modflag == 0) continue; /* skip unmodified slots */
1242
              db = select_slot(dbarr, curfile);
1253
              db = select_slot(dbarr, curfile);
1243
              if (ui_confirm_if_unsaved(db) != 0) {
1254
              if (ui_confirm_if_unsaved(db) != 0) {
1244
                quitnow = 0;
1255
                quitnow = 0;
1245
                break;
1256
                break;
1246
              }
1257
              }
1247
            }
1258
            }
1248
          }
1259
          }
1249
          break;
1260
          break;
1250
      }
1261
      }
1251
 
1262
 
1252
      if (quitnow) break;
1263
      if (quitnow) break;
1253
 
1264
 
1254
    } else if (k == 0x0D) { /* ENTER */
1265
    } else if (k == 0x0D) { /* ENTER */
1255
      unsigned short off = db->xoffset + db->cursorposx;
1266
      unsigned short off = db->xoffset + db->cursorposx;
1256
      /* add a new line */
1267
      /* add a new line */
1257
      if (line_add(db, db->cursor->payload + off, db->cursor->len - off) == 0) {
1268
      if (line_add(db, db->cursor->payload + off, db->cursor->len - off) == 0) {
1258
        db->modflag = 1;
1269
        db->modflag = 1;
1259
        db->cursor = db->cursor->prev; /* back to original line */
1270
        db->cursor = db->cursor->prev; /* back to original line */
1260
        db->curline -= 1;
1271
        db->curline -= 1;
1261
        /* trim the line above */
1272
        /* trim the line above */
1262
        db->cursor->len = off;
1273
        db->cursor->len = off;
1263
        /* move cursor to the (new) line below */
1274
        /* move cursor to the (new) line below */
1264
        uidirty.from = db->cursorposy;
1275
        uidirty.from = db->cursorposy;
1265
        uidirty.to = 0xff;
1276
        uidirty.to = 0xff;
1266
        cursor_down(db);
1277
        cursor_down(db);
1267
        cursor_home(db);
1278
        cursor_home(db);
1268
      } else {
1279
      } else {
1269
        /* ERROR: OUT OF MEMORY */
1280
        /* ERROR: OUT OF MEMORY */
1270
      }
1281
      }
1271
 
1282
 
1272
    } else if (k == 0x153) {  /* DEL */
1283
    } else if (k == 0x153) {  /* DEL */
1273
      del(db);
1284
      del(db);
1274
 
1285
 
1275
    } else if (k == 0x008) { /* BKSPC */
1286
    } else if (k == 0x008) { /* BKSPC */
1276
      bkspc(db);
1287
      bkspc(db);
1277
 
1288
 
1278
    } else if ((k >= 0x20) && (k <= 0xff)) { /* "normal" character */
1289
    } else if ((k >= 0x20) && (k <= 0xff)) { /* "normal" character */
1279
      char c = k;
1290
      char c = k;
1280
      insert_in_line(db, &c, 1);
1291
      insert_in_line(db, &c, 1);
1281
 
1292
 
1282
    } else if (k == 0x009) { /* TAB */
1293
    } else if (k == 0x009) { /* TAB */
1283
      if (glob_tablessmode == 0) {
1294
      if (glob_tablessmode == 0) {
1284
        insert_in_line(db, "        ", 8);
1295
        insert_in_line(db, "        ", 8);
1285
      } else {
1296
      } else {
1286
        insert_in_line(db, "\t", 1);
1297
        insert_in_line(db, "\t", 1);
1287
      }
1298
      }
1288
 
1299
 
1289
    } else if ((k >= 0x13b) && (k <= 0x144)) { /* F1..F10 */
1300
    } else if ((k >= 0x13b) && (k <= 0x144)) { /* F1..F10 */
1290
      db = select_slot(dbarr, k - 0x13b);
1301
      db = select_slot(dbarr, k - 0x13b);
1291
 
1302
 
1292
    } else if (k == 0x174) { /* CTRL+ArrRight - jump to next word */
1303
    } else if (k == 0x174) { /* CTRL+ArrRight - jump to next word */
1293
      /* if currently cursor is on a non-space, then fast-forward to nearest space or EOL */
1304
      /* if currently cursor is on a non-space, then fast-forward to nearest space or EOL */
1294
      for (;;) {
1305
      for (;;) {
1295
        if (db->xoffset + db->cursorposx == db->cursor->len) break;
1306
        if (db->xoffset + db->cursorposx == db->cursor->len) break;
1296
        if (db->cursor->payload[db->xoffset + db->cursorposx] == ' ') break;
1307
        if (db->cursor->payload[db->xoffset + db->cursorposx] == ' ') break;
1297
        cursor_right(db);
1308
        cursor_right(db);
1298
      }
1309
      }
1299
      /* now skip to next non-space or end of file */
1310
      /* now skip to next non-space or end of file */
1300
      for (;;) {
1311
      for (;;) {
1301
        cursor_right(db);
1312
        cursor_right(db);
1302
        if (db->cursor->payload[db->xoffset + db->cursorposx] != ' ') break;
1313
        if (db->cursor->payload[db->xoffset + db->cursorposx] != ' ') break;
1303
        if ((db->cursor->next == NULL) && (db->cursorposx + db->xoffset == db->cursor->len)) break;
1314
        if ((db->cursor->next == NULL) && (db->cursorposx + db->xoffset == db->cursor->len)) break;
1304
      }
1315
      }
1305
 
1316
 
1306
    } else if (k == 0x173) { /* CTRL+ArrLeft - jump to prev word */
1317
    } else if (k == 0x173) { /* CTRL+ArrLeft - jump to prev word */
1307
      cursor_left(db);
1318
      cursor_left(db);
1308
      /* if currently cursor is on a space, then fast-forward to nearest non-space or start of line */
1319
      /* if currently cursor is on a space, then fast-forward to nearest non-space or start of line */
1309
      for (;;) {
1320
      for (;;) {
1310
        if ((db->xoffset == 0) && (db->cursorposx == 0)) break;
1321
        if ((db->xoffset == 0) && (db->cursorposx == 0)) break;
1311
        if (db->cursor->payload[db->xoffset + db->cursorposx] != ' ') break;
1322
        if (db->cursor->payload[db->xoffset + db->cursorposx] != ' ') break;
1312
        cursor_left(db);
1323
        cursor_left(db);
1313
      }
1324
      }
1314
      /* now skip to next space or start of file */
1325
      /* now skip to next space or start of file */
1315
      for (;;) {
1326
      for (;;) {
1316
        cursor_left(db);
1327
        cursor_left(db);
1317
        if (db->cursor->payload[db->xoffset + db->cursorposx] == ' ') {
1328
        if (db->cursor->payload[db->xoffset + db->cursorposx] == ' ') {
1318
          cursor_right(db);
1329
          cursor_right(db);
1319
          break;
1330
          break;
1320
        }
1331
        }
1321
        if ((db->cursorposx == 0) && (db->xoffset == 0)) break;
1332
        if ((db->cursorposx == 0) && (db->xoffset == 0)) break;
1322
      }
1333
      }
1323
 
1334
 
1324
    } else if ((k == 0x003) || (k == 0x018)) { /* CTRL+C or CTRL+X */
1335
    } else if ((k == 0x003) || (k == 0x018)) { /* CTRL+C or CTRL+X */
1325
      /* free clipboard if anything in it */
1336
      /* free clipboard if anything in it */
1326
      if (clipboard != NULL) line_free(clipboard);
1337
      if (clipboard != NULL) line_free(clipboard);
1327
 
1338
 
1328
      /* copy cursor line to clipboard */
1339
      /* copy cursor line to clipboard */
1329
      clipboard = line_calloc(db->cursor->len);
1340
      clipboard = line_calloc(db->cursor->len);
1330
      if (clipboard == NULL) {
1341
      if (clipboard == NULL) {
1331
        ui_msg(10, 0, 0, SCHEME_ERR); /* ERROR */
1342
        ui_msg(10, 0, 0, SCHEME_ERR); /* ERROR */
1332
        mdr_bios_tickswait(18); /* 1s */
1343
        mdr_bios_tickswait(18); /* 1s */
1333
      } else {
1344
      } else {
1334
        mdr_cout_char_rep(db->cursorposy, 0, ' ', ((SCHEME_TEXT >> 4) | (SCHEME_TEXT << 4)) & 0xff, screenlastcol);
1345
        mdr_cout_char_rep(db->cursorposy, 0, ' ', ((SCHEME_TEXT >> 4) | (SCHEME_TEXT << 4)) & 0xff, screenlastcol);
1335
        uidirty.from = db->cursorposy;
1346
        uidirty.from = db->cursorposy;
1336
        uidirty.to = db->cursorposy;
1347
        uidirty.to = db->cursorposy;
1337
        if (db->cursor->len != 0) {
1348
        if (db->cursor->len != 0) {
1338
          fmemmove(clipboard->payload, db->cursor->payload, db->cursor->len);
1349
          fmemmove(clipboard->payload, db->cursor->payload, db->cursor->len);
1339
          clipboard->len = db->cursor->len;
1350
          clipboard->len = db->cursor->len;
1340
        }
1351
        }
1341
        mdr_bios_tickswait(2); /* ca 100ms */
1352
        mdr_bios_tickswait(2); /* ca 100ms */
1342
 
1353
 
1343
        /* if this is about cutting the line (CTRL+X) then delete cur line */
1354
        /* if this is about cutting the line (CTRL+X) then delete cur line */
1344
        if ((k == 0x018) && ((db->cursor->next != NULL) || (db->cursor->prev != NULL))) {
1355
        if ((k == 0x018) && ((db->cursor->next != NULL) || (db->cursor->prev != NULL))) {
1345
          if (db->cursor->next) db->cursor->next->prev = db->cursor->prev;
1356
          if (db->cursor->next) db->cursor->next->prev = db->cursor->prev;
1346
          if (db->cursor->prev) db->cursor->prev->next = db->cursor->next;
1357
          if (db->cursor->prev) db->cursor->prev->next = db->cursor->next;
1347
          clipboard->prev = db->cursor;
1358
          clipboard->prev = db->cursor;
1348
          if (db->cursor->next) {
1359
          if (db->cursor->next) {
1349
            db->cursor = db->cursor->next;
1360
            db->cursor = db->cursor->next;
1350
          } else {
1361
          } else {
1351
            cursor_up(db);
1362
            cursor_up(db);
1352
          }
1363
          }
1353
          line_free(clipboard->prev);
1364
          line_free(clipboard->prev);
1354
          db->totlines -= 1;
1365
          db->totlines -= 1;
1355
          db->modflag = 1;
1366
          db->modflag = 1;
1356
          uidirty.from = 0;
1367
          uidirty.from = 0;
1357
          uidirty.to = 0xff;
1368
          uidirty.to = 0xff;
1358
          recompute_curline(db);
1369
          recompute_curline(db);
1359
        }
1370
        }
1360
      }
1371
      }
1361
 
1372
 
1362
    } else if ((k == 0x016) && (clipboard != NULL)) { /* CTRL+V */
1373
    } else if ((k == 0x016) && (clipboard != NULL)) { /* CTRL+V */
1363
      if (line_add(db, clipboard->payload, clipboard->len) != 0) {
1374
      if (line_add(db, clipboard->payload, clipboard->len) != 0) {
1364
        ui_msg(10, 0, 0, SCHEME_ERR); /* ERROR */
1375
        ui_msg(10, 0, 0, SCHEME_ERR); /* ERROR */
1365
        mdr_bios_tickswait(18); /* 1s */
1376
        mdr_bios_tickswait(18); /* 1s */
1366
      } else {
1377
      } else {
1367
        /* rewire the linked list so the new line is on top of the previous one */
1378
        /* rewire the linked list so the new line is on top of the previous one */
1368
        clipboard->prev = db->cursor->prev;
1379
        clipboard->prev = db->cursor->prev;
1369
        /* remove prev node from list */
1380
        /* remove prev node from list */
1370
        db->cursor->prev = db->cursor->prev->prev;
1381
        db->cursor->prev = db->cursor->prev->prev;
1371
        if (db->cursor->prev != NULL) db->cursor->prev->next = db->cursor;
1382
        if (db->cursor->prev != NULL) db->cursor->prev->next = db->cursor;
1372
        /* insert the node after cursor now */
1383
        /* insert the node after cursor now */
1373
        clipboard->prev->next = db->cursor->next;
1384
        clipboard->prev->next = db->cursor->next;
1374
        if (db->cursor->next != NULL) db->cursor->next->prev = clipboard->prev;
1385
        if (db->cursor->next != NULL) db->cursor->next->prev = clipboard->prev;
1375
        clipboard->prev->prev = db->cursor;
1386
        clipboard->prev->prev = db->cursor;
1376
        db->cursor->next = clipboard->prev;
1387
        db->cursor->next = clipboard->prev;
1377
        cursor_down(db);
1388
        cursor_down(db);
1378
        db->modflag = 1;
1389
        db->modflag = 1;
1379
      }
1390
      }
1380
      uidirty.from = 0;
1391
      uidirty.from = 0;
1381
      uidirty.to = 0xff;
1392
      uidirty.to = 0xff;
1382
      recompute_curline(db);
1393
      recompute_curline(db);
1383
 
1394
 
1384
#ifdef DBG_UNHKEYS
1395
#ifdef DBG_UNHKEYS
1385
    } else { /* UNHANDLED KEY - TODO IGNORE THIS IN PRODUCTION RELEASE */
1396
    } else { /* UNHANDLED KEY - TODO IGNORE THIS IN PRODUCTION RELEASE */
1386
      char buff[4];
1397
      char buff[4];
1387
      const char *HEX = "0123456789ABCDEF";
1398
      const char *HEX = "0123456789ABCDEF";
1388
      buff[0] = HEX[(k >> 8) & 15];
1399
      buff[0] = HEX[(k >> 8) & 15];
1389
      buff[1] = HEX[(k >> 4) & 15];
1400
      buff[1] = HEX[(k >> 4) & 15];
1390
      buff[2] = HEX[k & 15];
1401
      buff[2] = HEX[k & 15];
1391
      mdr_cout_str(screenh - 1, 0, "UNHANDLED KEY: 0x", SCHEME_STBAR1, 17);
1402
      mdr_cout_str(screenh - 1, 0, "UNHANDLED KEY: 0x", SCHEME_STBAR1, 17);
1392
      mdr_cout_str(screenh - 1, 17, buff, SCHEME_STBAR1, 3);
1403
      mdr_cout_str(screenh - 1, 17, buff, SCHEME_STBAR1, 3);
1393
      mdr_dos_getkey2();
1404
      mdr_dos_getkey2();
1394
      break;
1405
      break;
1395
#endif
1406
#endif
1396
    }
1407
    }
1397
  }
1408
  }
1398
 
1409
 
1399
  mdr_cout_close();
1410
  mdr_cout_close();
1400
 
1411
 
1401
  /* restore the DOS BREAK flag if it was originally set */
1412
  /* restore the DOS BREAK flag if it was originally set */
1402
  if (original_breakflag != 0) mdr_dos_ctrlc_enable();
1413
  if (original_breakflag != 0) mdr_dos_ctrlc_enable();
1403
 
1414
 
1404
  /* no need to free memory, DOS will do it for me */
1415
  /* no need to free memory, DOS will do it for me */
1405
 
1416
 
1406
  return;
1417
  return;
1407
}
1418
}
1408
 
1419