Subversion Repositories SvarDOS

Rev

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

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