Subversion Repositories SvarDOS

Rev

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

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