Subversion Repositories SvarDOS

Rev

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

Rev 1331 Rev 1332
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 <dos.h>
26
#include <dos.h>
27
#include <fcntl.h>
27
#include <fcntl.h>
28
#include <stdlib.h>
28
#include <stdlib.h>
29
#include <string.h>
29
#include <string.h>
30
#include <malloc.h>   /* _fcalloc() */
30
#include <malloc.h>   /* _fcalloc() */
31
 
31
 
32
#include "mdr\bios.h"
32
#include "mdr\bios.h"
33
#include "mdr\cout.h"
33
#include "mdr\cout.h"
34
#include "mdr\dos.h"
34
#include "mdr\dos.h"
35
#include "mdr\keyb.h"
35
#include "mdr\keyb.h"
36
 
36
 
37
#include "svarlang\svarlang.h"
37
#include "svarlang\svarlang.h"
38
 
38
 
39
 
39
 
40
/*****************************************************************************
40
/*****************************************************************************
41
 * global variables and definitions                                          *
41
 * global variables and definitions                                          *
42
 *****************************************************************************/
42
 *****************************************************************************/
43
#define COL_TXT        0
43
#define COL_TXT        0
44
#define COL_STATUSBAR1 1
44
#define COL_STATUSBAR1 1
45
#define COL_STATUSBAR2 2
45
#define COL_STATUSBAR2 2
-
 
46
#define COL_STATUSBAR3 3
46
#define COL_SCROLLBAR  3
47
#define COL_SCROLLBAR  4
47
#define COL_MSG        4
48
#define COL_MSG        5
48
#define COL_ERR        5
49
#define COL_ERR        6
49
 
50
 
50
/* preload the mono scheme (to be overloaded at runtime if color adapter present) */
51
/* preload the mono scheme (to be overloaded at runtime if color adapter present) */
51
static unsigned char scheme[] = {0x07, 0x70, 0x70, 0x70, 0x70, 0xf0};
52
static unsigned char scheme[] = {0x07, 0x70, 0x70, 0xf0, 0x70, 0x70, 0xf0};
52
 
53
 
53
static unsigned char screenw, screenh;
54
static unsigned char screenw, screenh;
54
 
55
 
55
static struct {
56
static struct {
56
    unsigned char from;
57
    unsigned char from;
57
    unsigned char to;
58
    unsigned char to;
58
} uidirty = {0, 0xff}; /* make sure to redraw entire UI at first run */
59
} uidirty = {0, 0xff}; /* make sure to redraw entire UI at first run */
59
 
60
 
60
#define SCROLL_CURSOR 0xB1
61
#define SCROLL_CURSOR 0xB1
61
 
62
 
62
struct line {
63
struct line {
63
  struct line far *prev;
64
  struct line far *prev;
64
  struct line far *next;
65
  struct line far *next;
65
  unsigned short len;
66
  unsigned short len;
66
  char payload[1];
67
  char payload[1];
67
};
68
};
68
 
69
 
69
struct file {
70
struct file {
70
  int fd;
71
  int fd;
71
  struct line far *cursor;
72
  struct line far *cursor;
72
  unsigned short xoffset;
73
  unsigned short xoffset;
73
  unsigned char cursorposx;
74
  unsigned char cursorposx;
74
  unsigned char cursorposy;
75
  unsigned char cursorposy;
75
  unsigned short totlines;
76
  unsigned short totlines;
76
  unsigned short curline;
77
  unsigned short curline;
77
  char lfonly;   /* set if line endings are LF (CR/LF otherwise) */
78
  char lfonly;   /* set if line endings are LF (CR/LF otherwise) */
78
  char modflag;  /* non-zero if file has been modified since last save */
79
  char modflag;  /* non-zero if file has been modified since last save */
79
  char fname[1]; /* dynamically sized */
80
  char fname[128];
80
};
81
};
81
 
82
 
82
 
83
 
83
/*****************************************************************************
84
/*****************************************************************************
84
 * functions                                                                 *
85
 * functions                                                                 *
85
 *****************************************************************************/
86
 *****************************************************************************/
86
 
87
 
87
/* adds a new line at cursor position into file linked list and advance cursor
88
/* adds a new line at cursor position into file linked list and advance cursor
88
 * returns non-zero on error */
89
 * returns non-zero on error */
89
static int line_add(struct file *db, const char far *line, unsigned short slen) {
90
static int line_add(struct file *db, const char far *line, unsigned short slen) {
90
  struct line far *l;
91
  struct line far *l;
91
 
92
 
92
  l = _fcalloc(1, sizeof(struct line) + slen);
93
  l = _fcalloc(1, sizeof(struct line) + slen);
93
  if (l == NULL) return(-1);
94
  if (l == NULL) return(-1);
94
 
95
 
95
  l->prev = db->cursor;
96
  l->prev = db->cursor;
96
  if (db->cursor) {
97
  if (db->cursor) {
97
    l->next = db->cursor->next;
98
    l->next = db->cursor->next;
98
    db->cursor->next = l;
99
    db->cursor->next = l;
99
    l->next->prev = l;
100
    l->next->prev = l;
100
  }
101
  }
101
  db->cursor = l;
102
  db->cursor = l;
102
  _fmemcpy(l->payload, line, slen);
103
  _fmemcpy(l->payload, line, slen);
103
  l->len = slen;
104
  l->len = slen;
104
 
105
 
105
  db->totlines += 1;
106
  db->totlines += 1;
106
  db->curline += 1;
107
  db->curline += 1;
107
 
108
 
108
  return(0);
109
  return(0);
109
}
110
}
110
 
111
 
111
 
112
 
-
 
113
static void ui_getstring(const char *query, char *s, unsigned char maxlen) {
-
 
114
  unsigned char len = 0, y, x;
-
 
115
  int k;
-
 
116
 
-
 
117
  if (maxlen == 0) return;
-
 
118
  maxlen--; /* make room for the nul terminator */
-
 
119
 
-
 
120
  y = screenh - 1;
-
 
121
 
-
 
122
  /* print query string */
-
 
123
  x = mdr_cout_str(y, 0, query, scheme[COL_STATUSBAR3], 40);
-
 
124
  mdr_cout_char_rep(y, x++, ' ', scheme[COL_STATUSBAR3], screenw - x);
-
 
125
 
-
 
126
  for (;;) {
-
 
127
    mdr_cout_locate(y, x + len);
-
 
128
    k = keyb_getkey();
-
 
129
 
-
 
130
    if (k == 0x1b) return; /* ESC */
-
 
131
 
-
 
132
    if (k == '\r') {
-
 
133
      s[len] = 0;
-
 
134
      return;
-
 
135
    }
-
 
136
 
-
 
137
    if ((k == 0x08) && (len > 0)) { /* BKSPC */
-
 
138
      len--;
-
 
139
      mdr_cout_char(y, x + len, ' ', scheme[COL_STATUSBAR3]);
-
 
140
      continue;
-
 
141
    }
-
 
142
 
-
 
143
    if ((k <= 0xff) && (k >= ' ') && (len < maxlen)) {
-
 
144
      mdr_cout_char(y, x + len, k, scheme[COL_STATUSBAR3]);
-
 
145
      s[len++] = k;
-
 
146
    }
-
 
147
 
-
 
148
  }
-
 
149
}
-
 
150
 
-
 
151
 
112
/* append a nul-terminated string to line at cursor position */
152
/* append a nul-terminated string to line at cursor position */
113
static int line_append(struct file *f, const char far *buf, unsigned short len) {
153
static int line_append(struct file *f, const char far *buf, unsigned short len) {
114
  struct line far *n;
154
  struct line far *n;
115
  if (sizeof(struct line) + f->cursor->len + len < len) return(-1); /* overflow check */
155
  if (sizeof(struct line) + f->cursor->len + len < len) return(-1); /* overflow check */
116
  n = _frealloc(f->cursor, sizeof(struct line) + f->cursor->len + len);
156
  n = _frealloc(f->cursor, sizeof(struct line) + f->cursor->len + len);
117
  if (n == NULL) return(-1);
157
  if (n == NULL) return(-1);
118
  f->cursor = n;
158
  f->cursor = n;
119
  _fmemcpy(f->cursor->payload + f->cursor->len, buf, len);
159
  _fmemcpy(f->cursor->payload + f->cursor->len, buf, len);
120
  f->cursor->len += len;
160
  f->cursor->len += len;
121
 
161
 
122
  /* rewire the linked list */
162
  /* rewire the linked list */
123
  if (f->cursor->next) f->cursor->next->prev = f->cursor;
163
  if (f->cursor->next) f->cursor->next->prev = f->cursor;
124
  if (f->cursor->prev) f->cursor->prev->next = f->cursor;
164
  if (f->cursor->prev) f->cursor->prev->next = f->cursor;
125
 
165
 
126
  return(0);
166
  return(0);
127
}
167
}
128
 
168
 
129
 
169
 
130
static void db_rewind(struct file *db) {
170
static void db_rewind(struct file *db) {
131
  if (db->cursor == NULL) return;
171
  if (db->cursor == NULL) return;
132
  while (db->cursor->prev) db->cursor = db->cursor->prev;
172
  while (db->cursor->prev) db->cursor = db->cursor->prev;
133
  db->curline = 0;
173
  db->curline = 0;
134
}
174
}
135
 
175
 
136
 
176
 
137
static void load_colorscheme(void) {
177
static void load_colorscheme(void) {
138
  scheme[COL_TXT] = 0x17;
178
  scheme[COL_TXT] = 0x17;
139
  scheme[COL_STATUSBAR1] = 0x70;
179
  scheme[COL_STATUSBAR1] = 0x70;
140
  scheme[COL_STATUSBAR2] = 0x78;
180
  scheme[COL_STATUSBAR2] = 0x78;
-
 
181
  scheme[COL_STATUSBAR3] = 0xf0;
141
  scheme[COL_SCROLLBAR] = 0x70;
182
  scheme[COL_SCROLLBAR] = 0x70;
142
  scheme[COL_MSG] = 0xf0;
183
  scheme[COL_MSG] = 0xf0;
143
  scheme[COL_ERR] = 0x4f;
184
  scheme[COL_ERR] = 0x4f;
144
}
185
}
145
 
186
 
146
 
187
 
147
static void ui_basic(const struct file *db) {
188
static void ui_basic(const struct file *db) {
148
  const char *s = svarlang_strid(0); /* HELP */
189
  const char *s = svarlang_strid(0); /* HELP */
149
  unsigned char helpcol = screenw - (strlen(s) + 4);
190
  unsigned char helpcol = screenw - (strlen(s) + 4);
150
 
191
 
151
  /* fill status bar with background (without modflag as it is refreshed by ui_refresh) */
192
  /* fill status bar with background (without modflag as it is refreshed by ui_refresh) */
152
  mdr_cout_char_rep(screenh - 1, 1, ' ', scheme[COL_STATUSBAR1], screenw - 1);
193
  mdr_cout_char_rep(screenh - 1, 1, ' ', scheme[COL_STATUSBAR1], screenw - 1);
153
 
194
 
154
  /* filename */
195
  /* filename */
155
  {
196
  {
156
    const char *fn = db->fname;
197
    const char *fn = db->fname;
157
    if (*fn == 0) fn = svarlang_str(0, 1);
198
    if (*fn == 0) fn = svarlang_str(0, 1);
158
    mdr_cout_str(screenh - 1, 1, fn, scheme[COL_STATUSBAR1], screenw);
199
    mdr_cout_str(screenh - 1, 1, fn, scheme[COL_STATUSBAR1], screenw);
159
  }
200
  }
160
 
201
 
161
  /* eol type */
202
  /* eol type */
162
  {
203
  {
163
    const char *eoltype = "CRLF";
204
    const char *eoltype = "CRLF";
164
    if (db->lfonly) eoltype = "LF";
205
    if (db->lfonly) eoltype = "LF";
165
    mdr_cout_str(screenh - 1, helpcol - 5, eoltype, scheme[COL_STATUSBAR1], 5);
206
    mdr_cout_str(screenh - 1, helpcol - 5, eoltype, scheme[COL_STATUSBAR1], 5);
166
  }
207
  }
167
 
208
 
168
  mdr_cout_str(screenh - 1, helpcol, " F1=", scheme[COL_STATUSBAR2], 40);
209
  mdr_cout_str(screenh - 1, helpcol, " F1=", scheme[COL_STATUSBAR2], 40);
169
  mdr_cout_str(screenh - 1, helpcol + 4, s, scheme[COL_STATUSBAR2], 40);
210
  mdr_cout_str(screenh - 1, helpcol + 4, s, scheme[COL_STATUSBAR2], 40);
170
}
211
}
171
 
212
 
172
 
213
 
173
static void ui_msg(const char *msg1, const char *msg2, unsigned char attr) {
214
static void ui_msg(const char *msg1, const char *msg2, unsigned char attr) {
174
  unsigned short x, y, msglen, i;
215
  unsigned short x, y, msglen, i;
175
  unsigned char msg2flag = 0;
216
  unsigned char msg2flag = 0;
176
 
217
 
177
  msglen = strlen(msg1);
218
  msglen = strlen(msg1);
178
  if (msg2) {
219
  if (msg2) {
179
    msg2flag = 1;
220
    msg2flag = 1;
180
    i = strlen(msg2);
221
    i = strlen(msg2);
181
    if (i > msglen) msglen = i;
222
    if (i > msglen) msglen = i;
182
  }
223
  }
183
 
224
 
184
  y = (screenh - 6) >> 1;
225
  y = (screenh - 6) >> 1;
185
  x = (screenw - msglen - 4) >> 1;
226
  x = (screenw - msglen - 4) >> 1;
186
  for (i = y+2+msg2flag; i >= y; i--) mdr_cout_char_rep(i, x, ' ', attr, msglen + 2);
227
  for (i = y+2+msg2flag; i >= y; i--) mdr_cout_char_rep(i, x, ' ', attr, msglen + 2);
187
  x++;
228
  x++;
188
  mdr_cout_str(y+1, x, msg1, attr, msglen);
229
  mdr_cout_str(y+1, x, msg1, attr, msglen);
189
  if (msg2) mdr_cout_str(y+2, x, msg2, attr, msglen);
230
  if (msg2) mdr_cout_str(y+2, x, msg2, attr, msglen);
190
 
231
 
191
  if (uidirty.from > y) uidirty.from = y;
232
  if (uidirty.from > y) uidirty.from = y;
192
  if (uidirty.to < y+4) uidirty.to = y+4;
233
  if (uidirty.to < y+4) uidirty.to = y+4;
193
}
234
}
194
 
235
 
195
 
236
 
196
static void ui_help(void) {
237
static void ui_help(void) {
197
#define MAXLINLEN 35
238
#define MAXLINLEN 35
198
  unsigned short i, offset;
239
  unsigned short i, offset;
199
  offset = (screenw - MAXLINLEN + 2) >> 1;
240
  offset = (screenw - MAXLINLEN + 2) >> 1;
200
  mdr_cout_cursor_hide();
241
  mdr_cout_cursor_hide();
201
  for (i = 2; i <= 15; i++) {
242
  for (i = 2; i <= 15; i++) {
202
    mdr_cout_char_rep(i, offset - 2, ' ', scheme[COL_STATUSBAR1], MAXLINLEN + 2);
243
    mdr_cout_char_rep(i, offset - 2, ' ', scheme[COL_STATUSBAR1], MAXLINLEN + 2);
203
  }
244
  }
204
 
245
 
205
  mdr_cout_str(3, offset, svarlang_str(0, 0), scheme[COL_STATUSBAR1], MAXLINLEN);
246
  mdr_cout_str(3, offset, svarlang_str(0, 0), scheme[COL_STATUSBAR1], MAXLINLEN);
206
  for (i = 0; i <= 4; i++) {
247
  for (i = 0; i <= 4; i++) {
207
    mdr_cout_str(5 + i, offset, svarlang_str(8, i), scheme[COL_STATUSBAR1], MAXLINLEN);
248
    mdr_cout_str(5 + i, offset, svarlang_str(8, i), scheme[COL_STATUSBAR1], MAXLINLEN);
208
  }
249
  }
209
  mdr_cout_str(5 + 1 + i, offset, svarlang_str(8, 10), scheme[COL_STATUSBAR1], MAXLINLEN);
250
  mdr_cout_str(5 + 1 + i, offset, svarlang_str(8, 10), scheme[COL_STATUSBAR1], MAXLINLEN);
210
 
251
 
211
  /* Press any key */
252
  /* Press any key */
212
  mdr_cout_str(14, offset, svarlang_str(8, 11), scheme[COL_STATUSBAR1], MAXLINLEN);
253
  mdr_cout_str(14, offset, svarlang_str(8, 11), scheme[COL_STATUSBAR1], MAXLINLEN);
213
 
254
 
214
  keyb_getkey();
255
  keyb_getkey();
215
  mdr_cout_cursor_show();
256
  mdr_cout_cursor_show();
216
#undef MAXLINLEN
257
#undef MAXLINLEN
217
}
258
}
218
 
259
 
219
 
260
 
220
static void ui_refresh(const struct file *db) {
261
static void ui_refresh(const struct file *db) {
221
  unsigned char x;
262
  unsigned char x;
222
  const struct line far *l;
263
  const struct line far *l;
223
  unsigned char y = db->cursorposy;
264
  unsigned char y = db->cursorposy;
224
 
265
 
225
#ifdef DBG_REFRESH
266
#ifdef DBG_REFRESH
226
  static char m = 'a';
267
  static char m = 'a';
227
  m++;
268
  m++;
228
  if (m > 'z') m = 'a';
269
  if (m > 'z') m = 'a';
229
#endif
270
#endif
230
 
271
 
231
  /* rewind cursor line to first line that needs redrawing */
272
  /* rewind cursor line to first line that needs redrawing */
232
  for (l = db->cursor; y > uidirty.from; y--) l = l->prev;
273
  for (l = db->cursor; y > uidirty.from; y--) l = l->prev;
233
 
274
 
234
  /* iterate over lines and redraw whatever needs to be redrawn */
275
  /* iterate over lines and redraw whatever needs to be redrawn */
235
  for (; l != NULL; l = l->next, y++) {
276
  for (; l != NULL; l = l->next, y++) {
236
 
277
 
237
    /* skip lines that do not need to be refreshed */
278
    /* skip lines that do not need to be refreshed */
238
    if (y < uidirty.from) continue;
279
    if (y < uidirty.from) continue;
239
    if (y > uidirty.to) break;
280
    if (y > uidirty.to) break;
240
 
281
 
241
    x = 0;
282
    x = 0;
242
    if (db->xoffset < l->len) {
283
    if (db->xoffset < l->len) {
243
      unsigned char i, limit;
284
      unsigned char i, limit;
244
      if (l->len - db->xoffset < screenw) {
285
      if (l->len - db->xoffset < screenw) {
245
        limit = l->len;
286
        limit = l->len;
246
      } else {
287
      } else {
247
        limit = db->xoffset + screenw - 1;
288
        limit = db->xoffset + screenw - 1;
248
      }
289
      }
249
      for (i = db->xoffset; i < limit; i++) mdr_cout_char(y, x++, l->payload[i], scheme[COL_TXT]);
290
      for (i = db->xoffset; i < limit; i++) mdr_cout_char(y, x++, l->payload[i], scheme[COL_TXT]);
250
    }
291
    }
251
 
292
 
252
    /* write empty spaces until end of line */
293
    /* write empty spaces until end of line */
253
    if (x < screenw - 1) mdr_cout_char_rep(y, x, ' ', scheme[COL_TXT], screenw - 1 - x);
294
    if (x < screenw - 1) mdr_cout_char_rep(y, x, ' ', scheme[COL_TXT], screenw - 1 - x);
254
 
295
 
255
#ifdef DBG_REFRESH
296
#ifdef DBG_REFRESH
256
    mdr_cout_char(y, 0, m, scheme[COL_STATUSBAR1]);
297
    mdr_cout_char(y, 0, m, scheme[COL_STATUSBAR1]);
257
#endif
298
#endif
258
 
299
 
259
    if (y == screenh - 2) break;
300
    if (y == screenh - 2) break;
260
  }
301
  }
261
 
302
 
262
  /* fill all lines below if empty (and they need to be redrawn) */
303
  /* fill all lines below if empty (and they need to be redrawn) */
263
  if (l == NULL) {
304
  if (l == NULL) {
264
    while ((y < screenh - 1) && (y < uidirty.to)) {
305
    while ((y < screenh - 1) && (y < uidirty.to)) {
265
      mdr_cout_char_rep(y++, 0, ' ', scheme[COL_TXT], screenw - 1);
306
      mdr_cout_char_rep(y++, 0, ' ', scheme[COL_TXT], screenw - 1);
266
    }
307
    }
267
  }
308
  }
268
 
309
 
269
  /* "file changed" flag */
310
  /* "file changed" flag */
270
  {
311
  {
271
    char flg = ' ';
312
    char flg = ' ';
272
    if (db->modflag) flg = '*';
313
    if (db->modflag) flg = '*';
273
    mdr_cout_char(screenh - 1, 0, flg, scheme[COL_STATUSBAR1]);
314
    mdr_cout_char(screenh - 1, 0, flg, scheme[COL_STATUSBAR1]);
274
  }
315
  }
275
 
316
 
276
  /* scroll bar */
317
  /* scroll bar */
277
  for (y = 0; y < (screenh - 1); y++) {
318
  for (y = 0; y < (screenh - 1); y++) {
278
    mdr_cout_char(y, screenw - 1, SCROLL_CURSOR, scheme[COL_SCROLLBAR]);
319
    mdr_cout_char(y, screenw - 1, SCROLL_CURSOR, scheme[COL_SCROLLBAR]);
279
  }
320
  }
280
 
321
 
281
  /* scroll cursor */
322
  /* scroll cursor */
282
  if (db->totlines >= screenh) {
323
  if (db->totlines >= screenh) {
283
    unsigned short topline = db->curline - db->cursorposy;
324
    unsigned short topline = db->curline - db->cursorposy;
284
    unsigned short col;
325
    unsigned short col;
285
    unsigned short totlines = db->totlines - screenh + 1;
326
    unsigned short totlines = db->totlines - screenh + 1;
286
    if (db->totlines - screenh > screenh) {
327
    if (db->totlines - screenh > screenh) {
287
      col = topline / (totlines / (screenh - 1));
328
      col = topline / (totlines / (screenh - 1));
288
    } else {
329
    } else {
289
      col = topline * (screenh - 1) / totlines;
330
      col = topline * (screenh - 1) / totlines;
290
    }
331
    }
291
    if (col >= screenh - 1) col = screenh - 2;
332
    if (col >= screenh - 1) col = screenh - 2;
292
    mdr_cout_char(col, screenw - 1, ' ', scheme[COL_SCROLLBAR]);
333
    mdr_cout_char(col, screenw - 1, ' ', scheme[COL_SCROLLBAR]);
293
  }
334
  }
294
}
335
}
295
 
336
 
296
 
337
 
297
static void check_cursor_not_after_eol(struct file *db) {
338
static void check_cursor_not_after_eol(struct file *db) {
298
  if (db->xoffset + db->cursorposx <= db->cursor->len) return;
339
  if (db->xoffset + db->cursorposx <= db->cursor->len) return;
299
 
340
 
300
  if (db->cursor->len < db->xoffset) {
341
  if (db->cursor->len < db->xoffset) {
301
    db->cursorposx = 0;
342
    db->cursorposx = 0;
302
    db->xoffset = db->cursor->len;
343
    db->xoffset = db->cursor->len;
303
    uidirty.from = 0;
344
    uidirty.from = 0;
304
    uidirty.to = 0xff;
345
    uidirty.to = 0xff;
305
  } else {
346
  } else {
306
    db->cursorposx = db->cursor->len - db->xoffset;
347
    db->cursorposx = db->cursor->len - db->xoffset;
307
  }
348
  }
308
}
349
}
309
 
350
 
310
 
351
 
311
static void cursor_up(struct file *db) {
352
static void cursor_up(struct file *db) {
312
  if (db->cursor->prev != NULL) {
353
  if (db->cursor->prev != NULL) {
313
    db->curline -= 1;
354
    db->curline -= 1;
314
    db->cursor = db->cursor->prev;
355
    db->cursor = db->cursor->prev;
315
    if (db->cursorposy == 0) {
356
    if (db->cursorposy == 0) {
316
      uidirty.from = 0;
357
      uidirty.from = 0;
317
      uidirty.to = 0xff;
358
      uidirty.to = 0xff;
318
    } else {
359
    } else {
319
      db->cursorposy -= 1;
360
      db->cursorposy -= 1;
320
    }
361
    }
321
  }
362
  }
322
}
363
}
323
 
364
 
324
 
365
 
325
static void cursor_eol(struct file *db) {
366
static void cursor_eol(struct file *db) {
326
  /* adjust xoffset to make sure eol is visible on screen */
367
  /* adjust xoffset to make sure eol is visible on screen */
327
  if (db->xoffset > db->cursor->len) {
368
  if (db->xoffset > db->cursor->len) {
328
    db->xoffset = db->cursor->len - 1;
369
    db->xoffset = db->cursor->len - 1;
329
    uidirty.from = 0;
370
    uidirty.from = 0;
330
    uidirty.to = 0xff;
371
    uidirty.to = 0xff;
331
  }
372
  }
332
 
373
 
333
  if (db->xoffset + screenw - 1 <= db->cursor->len) {
374
  if (db->xoffset + screenw - 1 <= db->cursor->len) {
334
    db->xoffset = db->cursor->len - screenw + 2;
375
    db->xoffset = db->cursor->len - screenw + 2;
335
    uidirty.from = 0;
376
    uidirty.from = 0;
336
    uidirty.to = 0xff;
377
    uidirty.to = 0xff;
337
  }
378
  }
338
  db->cursorposx = db->cursor->len - db->xoffset;
379
  db->cursorposx = db->cursor->len - db->xoffset;
339
}
380
}
340
 
381
 
341
 
382
 
342
static void cursor_down(struct file *db) {
383
static void cursor_down(struct file *db) {
343
  if (db->cursor->next != NULL) {
384
  if (db->cursor->next != NULL) {
344
    db->curline += 1;
385
    db->curline += 1;
345
    db->cursor = db->cursor->next;
386
    db->cursor = db->cursor->next;
346
    if (db->cursorposy < screenh - 2) {
387
    if (db->cursorposy < screenh - 2) {
347
      db->cursorposy += 1;
388
      db->cursorposy += 1;
348
    } else {
389
    } else {
349
      uidirty.from = 0;
390
      uidirty.from = 0;
350
      uidirty.to = 0xff;
391
      uidirty.to = 0xff;
351
    }
392
    }
352
  }
393
  }
353
}
394
}
354
 
395
 
355
 
396
 
356
static void cursor_left(struct file *db) {
397
static void cursor_left(struct file *db) {
357
  if (db->cursorposx > 0) {
398
  if (db->cursorposx > 0) {
358
    db->cursorposx -= 1;
399
    db->cursorposx -= 1;
359
  } else if (db->xoffset > 0) {
400
  } else if (db->xoffset > 0) {
360
    db->xoffset -= 1;
401
    db->xoffset -= 1;
361
    uidirty.from = 0;
402
    uidirty.from = 0;
362
    uidirty.to = 0xff;
403
    uidirty.to = 0xff;
363
  } else if (db->cursor->prev != NULL) { /* jump to end of line above */
404
  } else if (db->cursor->prev != NULL) { /* jump to end of line above */
364
    cursor_up(db);
405
    cursor_up(db);
365
    cursor_eol(db);
406
    cursor_eol(db);
366
  }
407
  }
367
}
408
}
368
 
409
 
369
 
410
 
370
static void cursor_home(struct file *db) {
411
static void cursor_home(struct file *db) {
371
  db->cursorposx = 0;
412
  db->cursorposx = 0;
372
  if (db->xoffset != 0) {
413
  if (db->xoffset != 0) {
373
    db->xoffset = 0;
414
    db->xoffset = 0;
374
    uidirty.from = 0;
415
    uidirty.from = 0;
375
    uidirty.to = 0xff;
416
    uidirty.to = 0xff;
376
  }
417
  }
377
}
418
}
378
 
419
 
379
 
420
 
380
static void cursor_right(struct file *db) {
421
static void cursor_right(struct file *db) {
381
  if (db->cursor->len > db->xoffset + db->cursorposx) {
422
  if (db->cursor->len > db->xoffset + db->cursorposx) {
382
    if (db->cursorposx < screenw - 2) {
423
    if (db->cursorposx < screenw - 2) {
383
      db->cursorposx += 1;
424
      db->cursorposx += 1;
384
    } else {
425
    } else {
385
      db->xoffset += 1;
426
      db->xoffset += 1;
386
      uidirty.from = 0;
427
      uidirty.from = 0;
387
      uidirty.to = 0xff;
428
      uidirty.to = 0xff;
388
    }
429
    }
389
  } else {
430
  } else {
390
    cursor_down(db);
431
    cursor_down(db);
391
    cursor_home(db);
432
    cursor_home(db);
392
  }
433
  }
393
}
434
}
394
 
435
 
395
 
436
 
396
static void del(struct file *db) {
437
static void del(struct file *db) {
397
  if (db->cursorposx + db->xoffset < db->cursor->len) {
438
  if (db->cursorposx + db->xoffset < db->cursor->len) {
398
    _fmemmove(db->cursor->payload + db->cursorposx + db->xoffset, db->cursor->payload + db->cursorposx + db->xoffset + 1, db->cursor->len - db->cursorposx - db->xoffset);
439
    _fmemmove(db->cursor->payload + db->cursorposx + db->xoffset, db->cursor->payload + db->cursorposx + db->xoffset + 1, db->cursor->len - db->cursorposx - db->xoffset);
399
    db->cursor->len -= 1; /* do this AFTER memmove so the copy includes the nul terminator */
440
    db->cursor->len -= 1; /* do this AFTER memmove so the copy includes the nul terminator */
400
    uidirty.from = db->cursorposy;
441
    uidirty.from = db->cursorposy;
401
    uidirty.to = db->cursorposy;
442
    uidirty.to = db->cursorposy;
402
    db->modflag = 1;
443
    db->modflag = 1;
403
  } else if (db->cursor->next != NULL) { /* cursor is at end of line: merge current line with next one (if there is a next one) */
444
  } else if (db->cursor->next != NULL) { /* cursor is at end of line: merge current line with next one (if there is a next one) */
404
    struct line far *nextline = db->cursor->next;
445
    struct line far *nextline = db->cursor->next;
405
    if (db->cursor->next->len > 0) {
446
    if (db->cursor->next->len > 0) {
406
      void far *newptr = _frealloc(db->cursor, sizeof(struct line) + db->cursor->len + db->cursor->next->len + 1);
447
      void far *newptr = _frealloc(db->cursor, sizeof(struct line) + db->cursor->len + db->cursor->next->len + 1);
407
      if (newptr != NULL) {
448
      if (newptr != NULL) {
408
        db->cursor = newptr;
449
        db->cursor = newptr;
409
        _fmemcpy(db->cursor->payload + db->cursor->len, db->cursor->next->payload, db->cursor->next->len + 1);
450
        _fmemcpy(db->cursor->payload + db->cursor->len, db->cursor->next->payload, db->cursor->next->len + 1);
410
        db->cursor->len += db->cursor->next->len;
451
        db->cursor->len += db->cursor->next->len;
411
      }
452
      }
412
    }
453
    }
413
    db->cursor->next = db->cursor->next->next;
454
    db->cursor->next = db->cursor->next->next;
414
    db->cursor->next->prev = db->cursor;
455
    db->cursor->next->prev = db->cursor;
415
    if (db->cursor->prev != NULL) db->cursor->prev->next = db->cursor; /* in case realloc changed my pointer */
456
    if (db->cursor->prev != NULL) db->cursor->prev->next = db->cursor; /* in case realloc changed my pointer */
416
    _ffree(nextline);
457
    _ffree(nextline);
417
    uidirty.from = db->cursorposy;
458
    uidirty.from = db->cursorposy;
418
    uidirty.to = 0xff;
459
    uidirty.to = 0xff;
419
    db->totlines -= 1;
460
    db->totlines -= 1;
420
    db->modflag = 1;
461
    db->modflag = 1;
421
  }
462
  }
422
}
463
}
423
 
464
 
424
 
465
 
425
static void bkspc(struct file *db) {
466
static void bkspc(struct file *db) {
426
 
467
 
427
  /* backspace is basically "left + del", not applicable only if cursor is on 1st byte of the file */
468
  /* backspace is basically "left + del", not applicable only if cursor is on 1st byte of the file */
428
  if ((db->cursorposx == 0) && (db->xoffset == 0) && (db->cursor->prev == NULL)) return;
469
  if ((db->cursorposx == 0) && (db->xoffset == 0) && (db->cursor->prev == NULL)) return;
429
 
470
 
430
  cursor_left(db);
471
  cursor_left(db);
431
  del(db);
472
  del(db);
432
}
473
}
433
 
474
 
434
 
475
 
435
/* a custom argv-parsing routine that looks directly inside the PSP, avoids the need
476
/* a custom argv-parsing routine that looks directly inside the PSP, avoids the need
436
 * of argc and argv, saves some 330 bytes of binary size */
477
 * of argc and argv, saves some 330 bytes of binary size */
437
static char *parseargv(void) {
478
static char *parseargv(void) {
438
  char *tail = (void *)0x81; /* THIS WORKS ONLY IN SMALL MEMORY MODEL */
479
  char *tail = (void *)0x81; /* THIS WORKS ONLY IN SMALL MEMORY MODEL */
439
  unsigned char count = 0;
480
  unsigned char count = 0;
440
  char *argv[4];
481
  char *argv[4];
441
 
482
 
442
  while (count < 4) {
483
  while (count < 4) {
443
    /* jump to nearest arg */
484
    /* jump to nearest arg */
444
    while (*tail == ' ') {
485
    while (*tail == ' ') {
445
      *tail = 0;
486
      *tail = 0;
446
      tail++;
487
      tail++;
447
    }
488
    }
448
 
489
 
449
    if (*tail == '\r') {
490
    if (*tail == '\r') {
450
      *tail = 0;
491
      *tail = 0;
451
      break;
492
      break;
452
    }
493
    }
453
 
494
 
454
    argv[count++] = tail;
495
    argv[count++] = tail;
455
 
496
 
456
    /* jump to next delimiter */
497
    /* jump to next delimiter */
457
    while ((*tail != ' ') && (*tail != '\r')) tail++;
498
    while ((*tail != ' ') && (*tail != '\r')) tail++;
458
  }
499
  }
459
 
500
 
460
  /* check args now */
501
  /* check args now */
461
  if (count == 0) return("");
502
  if (count == 0) return("");
462
 
503
 
463
  return(argv[0]);
504
  return(argv[0]);
464
}
505
}
465
 
506
 
466
 
507
 
467
static struct file *loadfile(const char *fname) {
508
static struct file *loadfile(const char *fname) {
468
  char buff[512]; /* read one entire sector at a time (faster) */
509
  char buff[512]; /* read one entire sector at a time (faster) */
469
  char *buffptr;
510
  char *buffptr;
470
  unsigned int len, llen;
511
  unsigned int len, llen;
471
  int fd;
512
  int fd;
472
  unsigned char eolfound;
513
  unsigned char eolfound;
473
  struct file *db;
514
  struct file *db;
474
 
515
 
475
  len = strlen(fname) + 1;
516
  len = strlen(fname) + 1;
476
  db = calloc(1, sizeof(struct file) + len);
517
  db = calloc(1, sizeof(struct file));
477
  if (db == NULL) return(NULL);
518
  if (db == NULL) return(NULL);
478
  memcpy(db->fname, fname, len);
519
  memcpy(db->fname, fname, len);
479
 
520
 
480
  if (*fname == 0) goto SKIPLOADING;
521
  if (*fname == 0) goto SKIPLOADING;
481
 
522
 
482
  if (_dos_open(fname, O_RDONLY, &fd) != 0) {
523
  if (_dos_open(fname, O_RDONLY, &fd) != 0) {
483
    mdr_coutraw_puts("Failed to open file:");
524
    mdr_coutraw_puts("Failed to open file:");
484
    mdr_coutraw_puts(fname);
525
    mdr_coutraw_puts(fname);
485
    free(db);
526
    free(db);
486
    return(NULL);
527
    return(NULL);
487
  }
528
  }
488
 
529
 
489
  db->lfonly = 1;
530
  db->lfonly = 1;
490
 
531
 
491
  /* start by adding an empty line */
532
  /* start by adding an empty line */
492
  if (line_add(db, NULL, 0) != 0) {
533
  if (line_add(db, NULL, 0) != 0) {
493
    /* TODO ERROR HANDLING */
534
    /* TODO ERROR HANDLING */
494
  }
535
  }
495
 
536
 
496
  for (eolfound = 0;;) {
537
  for (eolfound = 0;;) {
497
    unsigned short consumedbytes;
538
    unsigned short consumedbytes;
498
 
539
 
499
    if ((_dos_read(fd, buff, sizeof(buff), &len) != 0) || (len == 0)) break;
540
    if ((_dos_read(fd, buff, sizeof(buff), &len) != 0) || (len == 0)) break;
500
    buffptr = buff;
541
    buffptr = buff;
501
 
542
 
502
    FINDLINE:
543
    FINDLINE:
503
 
544
 
504
    /* look for nearest \n */
545
    /* look for nearest \n */
505
    for (consumedbytes = 0;; consumedbytes++) {
546
    for (consumedbytes = 0;; consumedbytes++) {
506
      if (consumedbytes == len) {
547
      if (consumedbytes == len) {
507
        llen = consumedbytes;
548
        llen = consumedbytes;
508
        break;
549
        break;
509
      }
550
      }
510
      if (buffptr[consumedbytes] == '\r') {
551
      if (buffptr[consumedbytes] == '\r') {
511
        llen = consumedbytes;
552
        llen = consumedbytes;
512
        consumedbytes++;
553
        consumedbytes++;
513
        db->lfonly = 0;
554
        db->lfonly = 0;
514
        break;
555
        break;
515
      }
556
      }
516
      if (buffptr[consumedbytes] == '\n') {
557
      if (buffptr[consumedbytes] == '\n') {
517
        eolfound = 1;
558
        eolfound = 1;
518
        llen = consumedbytes;
559
        llen = consumedbytes;
519
        consumedbytes++;
560
        consumedbytes++;
520
        break;
561
        break;
521
      }
562
      }
522
    }
563
    }
523
 
564
 
524
    /* consumedbytes is the amount of bytes processed from buffptr,
565
    /* consumedbytes is the amount of bytes processed from buffptr,
525
     * llen is the length of line's payload (without its line terminator) */
566
     * llen is the length of line's payload (without its line terminator) */
526
 
567
 
527
    /* append content, if line is non-empty */
568
    /* append content, if line is non-empty */
528
    if ((llen > 0) && (line_append(db, buffptr, llen) != 0)) {
569
    if ((llen > 0) && (line_append(db, buffptr, llen) != 0)) {
529
      mdr_coutraw_puts("out of memory");
570
      mdr_coutraw_puts("out of memory");
530
      free(db);
571
      free(db);
531
      db = NULL;
572
      db = NULL;
532
      break;
573
      break;
533
    }
574
    }
534
 
575
 
535
    /* add a new line if necessary */
576
    /* add a new line if necessary */
536
    if (eolfound) {
577
    if (eolfound) {
537
      if (line_add(db, NULL, 0) != 0) {
578
      if (line_add(db, NULL, 0) != 0) {
538
      /* TODO ERROR HANDLING */
579
      /* TODO ERROR HANDLING */
539
        mdr_coutraw_puts("out of memory");
580
        mdr_coutraw_puts("out of memory");
540
        free(db);
581
        free(db);
541
        db = NULL;
582
        db = NULL;
542
        break;
583
        break;
543
      }
584
      }
544
      eolfound = 0;
585
      eolfound = 0;
545
    }
586
    }
546
 
587
 
547
    /* anything left? process the buffer leftover again */
588
    /* anything left? process the buffer leftover again */
548
    if (consumedbytes < len) {
589
    if (consumedbytes < len) {
549
      len -= consumedbytes;
590
      len -= consumedbytes;
550
      buffptr += consumedbytes;
591
      buffptr += consumedbytes;
551
      goto FINDLINE;
592
      goto FINDLINE;
552
    }
593
    }
553
 
594
 
554
  }
595
  }
555
 
596
 
556
  _dos_close(fd);
597
  _dos_close(fd);
557
 
598
 
558
  SKIPLOADING:
599
  SKIPLOADING:
559
 
600
 
560
  /* add an empty line at end if not present already, also rewind cursor to top of file */
601
  /* add an empty line at end if not present already, also rewind cursor to top of file */
561
  if (db != NULL) {
602
  if (db != NULL) {
562
    if ((db->cursor == NULL) || (db->cursor->len != 0)) line_add(db, NULL, 0);
603
    if ((db->cursor == NULL) || (db->cursor->len != 0)) line_add(db, NULL, 0);
563
    db_rewind(db);
604
    db_rewind(db);
564
  }
605
  }
565
 
606
 
566
  return(db);
607
  return(db);
567
}
608
}
568
 
609
 
569
 
610
 
570
static int savefile(const struct file *db) {
611
static int savefile(const struct file *db, const char *newfname) {
571
  int fd;
612
  int fd;
572
  const struct line far *l;
613
  const struct line far *l;
573
  unsigned bytes;
614
  unsigned bytes;
574
  unsigned char eollen;
615
  unsigned char eollen;
575
  unsigned char eolbuf[2];
616
  unsigned char eolbuf[2];
-
 
617
  int errflag = 0;
576
 
618
 
-
 
619
  /* either create a new file if newfname provided, or... */
-
 
620
  if (newfname) {
577
  if (_dos_open(db->fname, O_WRONLY, &fd) != 0) {
621
    if (_dos_creatnew(newfname, _A_NORMAL, &fd) != 0) return(-1);
578
    return(-1);
622
  } else { /* ...open db->fname */
-
 
623
    if (_dos_open(db->fname, O_WRONLY, &fd) != 0) return(-1);
579
  }
624
  }
580
 
625
 
581
  l = db->cursor;
626
  l = db->cursor;
582
  while (l->prev) l = l->prev;
627
  while (l->prev) l = l->prev;
583
 
628
 
584
  /* preset line terminators */
629
  /* preset line terminators */
585
  if (db->lfonly) {
630
  if (db->lfonly) {
586
    eolbuf[0] = '\n';
631
    eolbuf[0] = '\n';
587
    eollen = 1;
632
    eollen = 1;
588
  } else {
633
  } else {
589
    eolbuf[0] = '\r';
634
    eolbuf[0] = '\r';
590
    eolbuf[1] = '\n';
635
    eolbuf[1] = '\n';
591
    eollen = 2;
636
    eollen = 2;
592
  }
637
  }
593
 
638
 
594
  while (l) {
639
  while (l) {
595
    /* do not write the last empty line, it is only useful for edition */
640
    /* do not write the last empty line, it is only useful for edition */
596
    if (l->len != 0) {
641
    if (l->len != 0) {
597
      _dos_write(fd, l->payload, l->len, &bytes);
642
      errflag |= _dos_write(fd, l->payload, l->len, &bytes);
598
    } else if (l->next == NULL) {
643
    } else if (l->next == NULL) {
599
      break;
644
      break;
600
    }
645
    }
601
    _dos_write(fd, eolbuf, eollen, &bytes);
646
    errflag |= _dos_write(fd, eolbuf, eollen, &bytes);
602
    l = l->next;
647
    l = l->next;
603
  }
648
  }
604
 
649
 
605
  _dos_close(fd);
650
  errflag |= _dos_close(fd);
606
 
651
 
607
  return(0);
652
  return(errflag);
608
}
653
}
609
 
654
 
610
 
655
 
611
static void insert_in_line(struct file *db, const char *databuf, unsigned short len) {
656
static void insert_in_line(struct file *db, const char *databuf, unsigned short len) {
612
  struct line far *n;
657
  struct line far *n;
613
  n = _frealloc(db->cursor, sizeof(struct line) + db->cursor->len + len);
658
  n = _frealloc(db->cursor, sizeof(struct line) + db->cursor->len + len);
614
  if (n != NULL) {
659
  if (n != NULL) {
615
    unsigned short off = db->xoffset + db->cursorposx;
660
    unsigned short off = db->xoffset + db->cursorposx;
616
    db->modflag = 1;
661
    db->modflag = 1;
617
    if (n->prev) n->prev->next = n;
662
    if (n->prev) n->prev->next = n;
618
    if (n->next) n->next->prev = n;
663
    if (n->next) n->next->prev = n;
619
    db->cursor = n;
664
    db->cursor = n;
620
    _fmemmove(db->cursor->payload + off + len, db->cursor->payload + off, db->cursor->len - off + 1);
665
    _fmemmove(db->cursor->payload + off + len, db->cursor->payload + off, db->cursor->len - off + 1);
621
    db->cursor->len += len;
666
    db->cursor->len += len;
622
    uidirty.from = db->cursorposy;
667
    uidirty.from = db->cursorposy;
623
    uidirty.to = db->cursorposy;
668
    uidirty.to = db->cursorposy;
624
    while (len--) {
669
    while (len--) {
625
      db->cursor->payload[off++] = *databuf;
670
      db->cursor->payload[off++] = *databuf;
626
      databuf++;
671
      databuf++;
627
      cursor_right(db);
672
      cursor_right(db);
628
    }
673
    }
629
  }
674
  }
630
}
675
}
631
 
676
 
632
 
677
 
633
int main(void) {
678
int main(void) {
634
  const char *fname;
679
  const char *fname;
635
  struct file *db;
680
  struct file *db;
636
 
681
 
637
  {
682
  {
638
    char nlspath[128], lang[8];
683
    char nlspath[128], lang[8];
639
    svarlang_autoload_pathlist("sved", mdr_dos_getenv(nlspath, "NLSPATH", sizeof(nlspath)), mdr_dos_getenv(lang, "LANG", sizeof(lang)));
684
    svarlang_autoload_pathlist("sved", mdr_dos_getenv(nlspath, "NLSPATH", sizeof(nlspath)), mdr_dos_getenv(lang, "LANG", sizeof(lang)));
640
  }
685
  }
641
 
686
 
642
  fname = parseargv();
687
  fname = parseargv();
643
 
688
 
644
  if (fname == NULL) {
689
  if (fname == NULL) {
645
    mdr_coutraw_puts(svarlang_str(1,0)); /* usage: sved file.txt */
690
    mdr_coutraw_puts(svarlang_str(1,0)); /* usage: sved file.txt */
646
    return(0);
691
    return(0);
647
  }
692
  }
648
 
693
 
649
  /* load file */
694
  /* load file */
650
  db = loadfile(fname);
695
  db = loadfile(fname);
651
  if (db == NULL) return(1);
696
  if (db == NULL) return(1);
652
 
697
 
653
  if (mdr_cout_init(&screenw, &screenh)) load_colorscheme();
698
  if (mdr_cout_init(&screenw, &screenh)) load_colorscheme();
654
  ui_basic(db);
699
  ui_basic(db);
655
 
700
 
656
  for (;;) {
701
  for (;;) {
657
    int k;
702
    int k;
658
 
703
 
659
    check_cursor_not_after_eol(db);
704
    check_cursor_not_after_eol(db);
660
    mdr_cout_locate(db->cursorposy, db->cursorposx);
705
    mdr_cout_locate(db->cursorposy, db->cursorposx);
661
 
706
 
662
    if (uidirty.from != 0xff) {
707
    if (uidirty.from != 0xff) {
663
      ui_refresh(db);
708
      ui_refresh(db);
664
      uidirty.from = 0xff;
709
      uidirty.from = 0xff;
665
    }
710
    }
666
#ifdef DBG_LINENUM
711
#ifdef DBG_LINENUM
667
      {
712
      {
668
        char ddd[10];
713
        char ddd[10];
669
        db->curline += 1;
714
        db->curline += 1;
670
        ddd[0] = '0' + db->curline / 100;
715
        ddd[0] = '0' + db->curline / 100;
671
        ddd[1] = '0' + (db->curline % 100) / 10;
716
        ddd[1] = '0' + (db->curline % 100) / 10;
672
        ddd[2] = '0' + (db->curline % 10);
717
        ddd[2] = '0' + (db->curline % 10);
673
        db->curline -= 1;
718
        db->curline -= 1;
674
        ddd[3] = '/';
719
        ddd[3] = '/';
675
        ddd[4] = '0' + db->totlines / 100;
720
        ddd[4] = '0' + db->totlines / 100;
676
        ddd[5] = '0' + (db->totlines % 100) / 10;
721
        ddd[5] = '0' + (db->totlines % 100) / 10;
677
        ddd[6] = '0' + (db->totlines % 10);
722
        ddd[6] = '0' + (db->totlines % 10);
678
        ddd[7] = 0;
723
        ddd[7] = 0;
679
        mdr_cout_str(screenh - 1, 40, ddd, scheme[COL_STATUSBAR1], sizeof(ddd));
724
        mdr_cout_str(screenh - 1, 40, ddd, scheme[COL_STATUSBAR1], sizeof(ddd));
680
      }
725
      }
681
#endif
726
#endif
682
 
727
 
683
    k = keyb_getkey();
728
    k = keyb_getkey();
684
 
729
 
685
    if (k == 0x150) { /* down */
730
    if (k == 0x150) { /* down */
686
      cursor_down(db);
731
      cursor_down(db);
687
 
732
 
688
    } else if (k == 0x148) { /* up */
733
    } else if (k == 0x148) { /* up */
689
      cursor_up(db);
734
      cursor_up(db);
690
 
735
 
691
    } else if (k == 0x14D) { /* right */
736
    } else if (k == 0x14D) { /* right */
692
      cursor_right(db);
737
      cursor_right(db);
693
 
738
 
694
    } else if (k == 0x14B) { /* left */
739
    } else if (k == 0x14B) { /* left */
695
      cursor_left(db);
740
      cursor_left(db);
696
 
741
 
697
    } else if (k == 0x149) { /* pgup */
742
    } else if (k == 0x149) { /* pgup */
698
      // TODO
743
      // TODO
699
 
744
 
700
    } else if (k == 0x151) { /* pgdown */
745
    } else if (k == 0x151) { /* pgdown */
701
      // TODO
746
      // TODO
702
 
747
 
703
    } else if (k == 0x147) { /* home */
748
    } else if (k == 0x147) { /* home */
704
       cursor_home(db);
749
       cursor_home(db);
705
 
750
 
706
    } else if (k == 0x14F) { /* end */
751
    } else if (k == 0x14F) { /* end */
707
       cursor_eol(db);
752
       cursor_eol(db);
708
 
753
 
709
    } else if (k == 0x1B) { /* ESC */
754
    } else if (k == 0x1B) { /* ESC */
710
      if (db->modflag == 0) break;
755
      if (db->modflag == 0) break;
711
      /* if file has been modified then ask for confirmation */
756
      /* if file has been modified then ask for confirmation */
712
      ui_msg(svarlang_str(0,4), svarlang_str(0,5), scheme[COL_MSG]);
757
      ui_msg(svarlang_str(0,4), svarlang_str(0,5), scheme[COL_MSG]);
713
      if (keyb_getkey() == '\r') break;
758
      if (keyb_getkey() == '\r') break;
714
 
759
 
715
    } else if (k == 0x0D) { /* ENTER */
760
    } else if (k == 0x0D) { /* ENTER */
716
      unsigned short off = db->xoffset + db->cursorposx;
761
      unsigned short off = db->xoffset + db->cursorposx;
717
      /* add a new line */
762
      /* add a new line */
718
      if (line_add(db, db->cursor->payload + off, db->cursor->len - off) == 0) {
763
      if (line_add(db, db->cursor->payload + off, db->cursor->len - off) == 0) {
719
        db->modflag = 1;
764
        db->modflag = 1;
720
        db->cursor = db->cursor->prev; /* back to original line */
765
        db->cursor = db->cursor->prev; /* back to original line */
721
        db->curline -= 1;
766
        db->curline -= 1;
722
        /* trim the line above */
767
        /* trim the line above */
723
        db->cursor->len = off;
768
        db->cursor->len = off;
724
        /* move cursor to the (new) line below */
769
        /* move cursor to the (new) line below */
725
        uidirty.from = db->cursorposy;
770
        uidirty.from = db->cursorposy;
726
        uidirty.to = 0xff;
771
        uidirty.to = 0xff;
727
        cursor_down(db);
772
        cursor_down(db);
728
        cursor_home(db);
773
        cursor_home(db);
729
      } else {
774
      } else {
730
        /* ERROR: OUT OF MEMORY */
775
        /* ERROR: OUT OF MEMORY */
731
      }
776
      }
732
 
777
 
733
    } else if (k == 0x153) {  /* DEL */
778
    } else if (k == 0x153) {  /* DEL */
734
      del(db);
779
      del(db);
735
 
780
 
736
    } else if (k == 0x008) { /* BKSPC */
781
    } else if (k == 0x008) { /* BKSPC */
737
      bkspc(db);
782
      bkspc(db);
738
 
783
 
739
    } else if ((k >= 0x20) && (k <= 0xff)) { /* "normal" character */
784
    } else if ((k >= 0x20) && (k <= 0xff)) { /* "normal" character */
740
      char c = k;
785
      char c = k;
741
      insert_in_line(db, &c, 1);
786
      insert_in_line(db, &c, 1);
742
 
787
 
743
    } else if (k == 0x009) { /* TAB */
788
    } else if (k == 0x009) { /* TAB */
744
      const char *tab = "        ";
789
      const char *tab = "        ";
745
      insert_in_line(db, tab, 8);
790
      insert_in_line(db, tab, 8);
746
 
791
 
747
    } else if (k == 0x13b) { /* F1 */
792
    } else if (k == 0x13b) { /* F1 */
748
      ui_help();
793
      ui_help();
749
      uidirty.from = 0;
794
      uidirty.from = 0;
750
      uidirty.to = 0xff;
795
      uidirty.to = 0xff;
751
 
796
 
752
    } else if (k == 0x13f) { /* F5 */
797
    } else if ((k == 0x13f) || (k == 0x140)) { /* F5 or F6 */
-
 
798
      int saveres;
-
 
799
 
-
 
800
      if ((k == 0x140) || (db->fname[0] == 0)) { /* save as... */
-
 
801
        char fname[25];
-
 
802
        ui_getstring(svarlang_str(0,6), fname, sizeof(fname));
-
 
803
        if (*fname == 0) continue;
-
 
804
        saveres = savefile(db, fname);
-
 
805
        if (saveres == 0) memcpy(db->fname, fname, sizeof(fname));
-
 
806
      } else {
-
 
807
        saveres = savefile(db, NULL);
-
 
808
      }
-
 
809
 
753
      if (savefile(db) == 0) {
810
      if (saveres == 0) {
754
        db->modflag = 0;
811
        db->modflag = 0;
755
        ui_msg(svarlang_str(0, 2), NULL, scheme[COL_MSG]);
812
        ui_msg(svarlang_str(0, 2), NULL, scheme[COL_MSG]);
756
        mdr_bios_tickswait(11); /* 11 ticks is about 600 ms */
813
        mdr_bios_tickswait(11); /* 11 ticks is about 600 ms */
757
      } else {
814
      } else {
758
        ui_msg(svarlang_str(0, 3), NULL, scheme[COL_ERR]);
815
        ui_msg(svarlang_str(0, 3), NULL, scheme[COL_ERR]);
759
        mdr_bios_tickswait(36); /* 2s */
816
        mdr_bios_tickswait(36); /* 2s */
760
      }
817
      }
761
 
818
 
-
 
819
      ui_basic(db);
-
 
820
      ui_refresh(db);
-
 
821
 
762
    } else if (k == 0x144) { /* F10 */
822
    } else if (k == 0x144) { /* F10 */
763
      db->modflag = 1;
823
      db->modflag = 1;
764
      db->lfonly ^= 1;
824
      db->lfonly ^= 1;
765
      ui_basic(db);
825
      ui_basic(db);
766
 
826
 
767
    } else if (k == 0x174) { /* CTRL+ArrRight - jump to next word */
827
    } else if (k == 0x174) { /* CTRL+ArrRight - jump to next word */
768
      /* if currently cursor is on a non-space, then fast-forward to nearest space or EOL */
828
      /* if currently cursor is on a non-space, then fast-forward to nearest space or EOL */
769
      for (;;) {
829
      for (;;) {
770
        if (db->xoffset + db->cursorposx == db->cursor->len) break;
830
        if (db->xoffset + db->cursorposx == db->cursor->len) break;
771
        if (db->cursor->payload[db->xoffset + db->cursorposx] == ' ') break;
831
        if (db->cursor->payload[db->xoffset + db->cursorposx] == ' ') break;
772
        cursor_right(db);
832
        cursor_right(db);
773
      }
833
      }
774
      /* now skip to next non-space or end of file */
834
      /* now skip to next non-space or end of file */
775
      for (;;) {
835
      for (;;) {
776
        cursor_right(db);
836
        cursor_right(db);
777
        if (db->cursor->payload[db->xoffset + db->cursorposx] != ' ') break;
837
        if (db->cursor->payload[db->xoffset + db->cursorposx] != ' ') break;
778
        if ((db->cursor->next == NULL) && (db->cursorposx + db->xoffset == db->cursor->len)) break;
838
        if ((db->cursor->next == NULL) && (db->cursorposx + db->xoffset == db->cursor->len)) break;
779
      }
839
      }
780
 
840
 
781
    } else if (k == 0x173) { /* CTRL+ArrLeft - jump to prev word */
841
    } else if (k == 0x173) { /* CTRL+ArrLeft - jump to prev word */
782
      cursor_left(db);
842
      cursor_left(db);
783
      /* if currently cursor is on a space, then fast-forward to nearest non-space or start of line */
843
      /* if currently cursor is on a space, then fast-forward to nearest non-space or start of line */
784
      for (;;) {
844
      for (;;) {
785
        if ((db->xoffset == 0) && (db->cursorposx == 0)) break;
845
        if ((db->xoffset == 0) && (db->cursorposx == 0)) break;
786
        if (db->cursor->payload[db->xoffset + db->cursorposx] != ' ') break;
846
        if (db->cursor->payload[db->xoffset + db->cursorposx] != ' ') break;
787
        cursor_left(db);
847
        cursor_left(db);
788
      }
848
      }
789
      /* now skip to next space or start of file */
849
      /* now skip to next space or start of file */
790
      for (;;) {
850
      for (;;) {
791
        cursor_left(db);
851
        cursor_left(db);
792
        if (db->cursor->payload[db->xoffset + db->cursorposx] == ' ') {
852
        if (db->cursor->payload[db->xoffset + db->cursorposx] == ' ') {
793
          cursor_right(db);
853
          cursor_right(db);
794
          break;
854
          break;
795
        }
855
        }
796
        if ((db->cursorposx == 0) && (db->xoffset == 0)) break;
856
        if ((db->cursorposx == 0) && (db->xoffset == 0)) break;
797
      }
857
      }
798
 
858
 
799
    } else { /* UNHANDLED KEY - TODO IGNORE THIS IN PRODUCTION RELEASE */
859
    } else { /* UNHANDLED KEY - TODO IGNORE THIS IN PRODUCTION RELEASE */
800
      char buff[4];
860
      char buff[4];
801
      const char *HEX = "0123456789ABCDEF";
861
      const char *HEX = "0123456789ABCDEF";
802
      buff[0] = HEX[(k >> 8) & 15];
862
      buff[0] = HEX[(k >> 8) & 15];
803
      buff[1] = HEX[(k >> 4) & 15];
863
      buff[1] = HEX[(k >> 4) & 15];
804
      buff[2] = HEX[k & 15];
864
      buff[2] = HEX[k & 15];
805
      mdr_cout_str(screenh - 1, 0, "UNHANDLED KEY: 0x", scheme[COL_STATUSBAR1], 17);
865
      mdr_cout_str(screenh - 1, 0, "UNHANDLED KEY: 0x", scheme[COL_STATUSBAR1], 17);
806
      mdr_cout_str(screenh - 1, 17, buff, scheme[COL_STATUSBAR1], 3);
866
      mdr_cout_str(screenh - 1, 17, buff, scheme[COL_STATUSBAR1], 3);
807
      keyb_getkey();
867
      keyb_getkey();
808
      break;
868
      break;
809
    }
869
    }
810
  }
870
  }
811
 
871
 
812
  mdr_cout_close();
872
  mdr_cout_close();
813
 
873
 
814
  /* no need to free memory, DOS will do it for me */
874
  /* no need to free memory, DOS will do it for me */
815
 
875
 
816
  return(0);
876
  return(0);
817
}
877
}
818
 
878