Subversion Repositories SvarDOS

Rev

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

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