Subversion Repositories SvarDOS

Rev

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

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