Subversion Repositories SvarDOS

Rev

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

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