Subversion Repositories SvarDOS

Rev

Rev 1577 | Rev 1579 | Go to most recent revision | Only display areas with differences | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed

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