Subversion Repositories SvarDOS

Rev

Rev 1775 | Only display areas with differences | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

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