Subversion Repositories SvarDOS

Rev

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

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