Subversion Repositories SvarDOS

Rev

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

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