/[LeafOK_CVS]/lbbs/src/editor.c
ViewVC logotype

Contents of /lbbs/src/editor.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.42 - (show annotations)
Tue Sep 30 06:05:48 2025 UTC (5 months, 2 weeks ago) by sysadm
Branch: MAIN
Changes since 1.41: +1 -1 lines
Content type: text/x-csrc
Fix bug in setting column position while change line at non-bottom line

1 /***************************************************************************
2 editor.h - description
3 -------------------
4 copyright : (C) 2004-2025 by Leaflet
5 email : leaflet@leafok.com
6 ***************************************************************************/
7
8 /***************************************************************************
9 * *
10 * This program is free software; you can redistribute it and/or modify *
11 * it under the terms of the GNU General Public License as published by *
12 * the Free Software Foundation; either version 3 of the License, or *
13 * (at your option) any later version. *
14 * *
15 ***************************************************************************/
16
17 #include "bbs.h"
18 #include "common.h"
19 #include "editor.h"
20 #include "io.h"
21 #include "log.h"
22 #include "memory_pool.h"
23 #include "str_process.h"
24 #include <stdlib.h>
25 #include <string.h>
26 #include <sys/param.h>
27
28 #define EDITOR_ESC_DISPLAY_STR "\033[32m*\033[m"
29 #define EDITOR_MEM_POOL_LINE_PER_CHUNK 1000
30 #define EDITOR_MEM_POOL_CHUNK_LIMIT (MAX_EDITOR_DATA_LINES / EDITOR_MEM_POOL_LINE_PER_CHUNK + 1)
31
32 static MEMORY_POOL *p_mp_data_line;
33 static MEMORY_POOL *p_mp_editor_data;
34
35 int editor_memory_pool_init(void)
36 {
37 if (p_mp_data_line != NULL || p_mp_editor_data != NULL)
38 {
39 log_error("Editor mem pool already initialized\n");
40 return -1;
41 }
42
43 p_mp_data_line = memory_pool_init(MAX_EDITOR_DATA_LINE_LENGTH, EDITOR_MEM_POOL_LINE_PER_CHUNK, EDITOR_MEM_POOL_CHUNK_LIMIT);
44 if (p_mp_data_line == NULL)
45 {
46 log_error("Memory pool init error\n");
47 return -2;
48 }
49
50 p_mp_editor_data = memory_pool_init(sizeof(EDITOR_DATA), 1, 1);
51 if (p_mp_data_line == NULL)
52 {
53 log_error("Memory pool init error\n");
54 return -3;
55 }
56
57 return 0;
58 }
59
60 void editor_memory_pool_cleanup(void)
61 {
62 if (p_mp_data_line != NULL)
63 {
64 memory_pool_cleanup(p_mp_data_line);
65 p_mp_data_line = NULL;
66 }
67
68 if (p_mp_editor_data != NULL)
69 {
70 memory_pool_cleanup(p_mp_editor_data);
71 p_mp_editor_data = NULL;
72 }
73 }
74
75 EDITOR_DATA *editor_data_load(const char *p_data)
76 {
77 EDITOR_DATA *p_editor_data;
78 char *p_data_line = NULL;
79 long line_offsets[MAX_EDITOR_DATA_LINES + 1];
80 long current_data_line_length = 0;
81 long i;
82
83 if (p_data == NULL)
84 {
85 log_error("editor_data_load() error: NULL pointer\n");
86 return NULL;
87 }
88
89 p_editor_data = memory_pool_alloc(p_mp_editor_data);
90 if (p_editor_data == NULL)
91 {
92 log_error("memory_pool_alloc() error\n");
93 return NULL;
94 }
95
96 p_editor_data->display_line_total = split_data_lines(p_data, SCREEN_COLS, line_offsets, MAX_EDITOR_DATA_LINES + 1,
97 0, p_editor_data->display_line_widths);
98
99 for (i = 0; i < p_editor_data->display_line_total; i++)
100 {
101 p_editor_data->display_line_lengths[i] = line_offsets[i + 1] - line_offsets[i];
102
103 if (i == 0 ||
104 current_data_line_length + p_editor_data->display_line_lengths[i] + 1 > MAX_EDITOR_DATA_LINE_LENGTH ||
105 (p_editor_data->display_line_lengths[i - 1] > 0 && p_data[line_offsets[i - 1] + p_editor_data->display_line_lengths[i - 1] - 1] == '\n'))
106 {
107 // Allocate new data line
108 p_data_line = memory_pool_alloc(p_mp_data_line);
109 if (p_data_line == NULL)
110 {
111 log_error("memory_pool_alloc() error: i = %d\n", i);
112 // Cleanup
113 editor_data_cleanup(p_editor_data);
114 return NULL;
115 }
116
117 p_editor_data->p_display_lines[i] = p_data_line;
118 current_data_line_length = 0;
119 }
120 else
121 {
122 p_editor_data->p_display_lines[i] = p_editor_data->p_display_lines[i - 1] + p_editor_data->display_line_lengths[i - 1];
123 }
124
125 memcpy(p_editor_data->p_display_lines[i], p_data + line_offsets[i], (size_t)p_editor_data->display_line_lengths[i]);
126 current_data_line_length += p_editor_data->display_line_lengths[i];
127
128 // Trim \n from last line
129 if (i + 1 == p_editor_data->display_line_total &&
130 p_editor_data->display_line_lengths[i] > 0 &&
131 p_editor_data->p_display_lines[i][p_editor_data->display_line_lengths[i] - 1] == '\n')
132 {
133 p_editor_data->display_line_lengths[i]--;
134 p_editor_data->display_line_widths[i]--;
135 current_data_line_length--;
136 }
137 p_data_line[current_data_line_length] = '\0';
138 }
139
140 memset(p_editor_data->p_display_lines + p_editor_data->display_line_total, 0, MAX_EDITOR_DATA_LINES - (size_t)p_editor_data->display_line_total);
141
142 return p_editor_data;
143 }
144
145 long editor_data_save(const EDITOR_DATA *p_editor_data, char *p_data, size_t buf_len)
146 {
147 long current_pos = 0;
148 long i;
149
150 if (p_editor_data == NULL || p_data == NULL)
151 {
152 log_error("editor_data_save() error: NULL pointer\n");
153 return -1;
154 }
155
156 for (i = 0; i < p_editor_data->display_line_total; i++)
157 {
158 if (current_pos + p_editor_data->display_line_lengths[i] + 1 > buf_len)
159 {
160 log_error("Data buffer not longer enough %d > %d\n", current_pos + p_editor_data->display_line_lengths[i] + 1, buf_len);
161 p_data[current_pos] = '\0';
162 return -2;
163 }
164
165 memcpy(p_data + current_pos, p_editor_data->p_display_lines[i], (size_t)p_editor_data->display_line_lengths[i]);
166 current_pos += p_editor_data->display_line_lengths[i];
167 }
168
169 p_data[current_pos] = '\0';
170
171 return current_pos;
172 }
173
174 void editor_data_cleanup(EDITOR_DATA *p_editor_data)
175 {
176 char *p_data_line = NULL;
177 long i;
178
179 if (p_editor_data == NULL)
180 {
181 return;
182 }
183
184 for (i = 0; i < p_editor_data->display_line_total; i++)
185 {
186 if (p_data_line == NULL)
187 {
188 p_data_line = p_editor_data->p_display_lines[i];
189 }
190
191 if (p_editor_data->display_line_lengths[i] > 0 &&
192 p_editor_data->p_display_lines[i][p_editor_data->display_line_lengths[i] - 1] == '\n')
193 {
194 memory_pool_free(p_mp_data_line, p_data_line);
195 p_data_line = NULL;
196 }
197 }
198
199 if (p_data_line != NULL)
200 {
201 memory_pool_free(p_mp_data_line, p_data_line);
202 }
203
204 memory_pool_free(p_mp_editor_data, p_editor_data);
205 }
206
207 int editor_data_insert(EDITOR_DATA *p_editor_data, long *p_display_line, long *p_offset,
208 const char *str, int str_len, long *p_last_updated_line)
209 {
210 long display_line = *p_display_line;
211 long offset = *p_offset;
212 char *p_data_line = NULL;
213 long len_data_line;
214 long offset_data_line;
215 long last_display_line; // of data line
216 long line_offsets[MAX_EDITOR_DATA_LINE_LENGTH + 1];
217 int line_widths[MAX_EDITOR_DATA_LINE_LENGTH + 1];
218 long split_line_total;
219 long i;
220 int len;
221 int eol;
222 int display_len;
223
224 if (p_editor_data == NULL || p_last_updated_line == NULL)
225 {
226 log_error("editor_data_op() error: NULL pointer\n");
227 return -1;
228 }
229
230 // Get length of current data line
231 len_data_line = 0;
232 p_data_line = p_editor_data->p_display_lines[display_line];
233 for (i = display_line - 1; i >= 0; i--)
234 {
235 if (p_editor_data->display_line_lengths[i] > 0 &&
236 p_editor_data->p_display_lines[i][p_editor_data->display_line_lengths[i] - 1] == '\n') // reach end of prior data line
237 {
238 break;
239 }
240
241 len_data_line += p_editor_data->display_line_lengths[i];
242 p_data_line = p_editor_data->p_display_lines[i];
243 }
244 offset_data_line = len_data_line + offset;
245 last_display_line = p_editor_data->display_line_total - 1;
246 for (i = display_line; i < p_editor_data->display_line_total; i++)
247 {
248 len_data_line += p_editor_data->display_line_lengths[i];
249
250 if (p_editor_data->display_line_lengths[i] > 0 &&
251 p_editor_data->p_display_lines[i][p_editor_data->display_line_lengths[i] - 1] == '\n') // reach end of current data line
252 {
253 last_display_line = i;
254 break;
255 }
256 }
257
258 // Split current data line if over-length
259 if (len_data_line + str_len + 2 > MAX_EDITOR_DATA_LINE_LENGTH || str[0] == CR)
260 {
261 if (p_editor_data->display_line_total >= MAX_EDITOR_DATA_LINES)
262 {
263 #ifdef _DEBUG
264 log_error("Split line error, display_line_total(%ld) reach limit(%d)\n",
265 p_editor_data->display_line_total, MAX_EDITOR_DATA_LINES);
266 #endif
267
268 return -2;
269 }
270
271 // Allocate new data line
272 p_data_line = memory_pool_alloc(p_mp_data_line);
273 if (p_data_line == NULL)
274 {
275 log_error("memory_pool_alloc() error\n");
276 return -2;
277 }
278
279 if (offset_data_line + str_len + 2 >= MAX_EDITOR_DATA_LINE_LENGTH || str[0] == CR)
280 {
281 if (str[0] == CR)
282 {
283 str_len = 0;
284 }
285
286 // Copy str to new data line
287 memcpy(p_data_line, str, (size_t)str_len);
288
289 // Copy rest part of current data line to new data line
290 memcpy(p_data_line + str_len,
291 p_editor_data->p_display_lines[display_line] + offset,
292 (size_t)(len_data_line - offset_data_line));
293
294 p_data_line[str_len + len_data_line - offset_data_line] = '\0';
295
296 // Add line ending to current display line (data line)
297 p_editor_data->p_display_lines[display_line][offset] = '\n';
298 p_editor_data->p_display_lines[display_line][offset + 1] = '\0';
299 p_editor_data->display_line_lengths[display_line] = offset + 1;
300
301 *p_display_line = display_line + 1;
302 *p_offset = str_len;
303 }
304 else
305 {
306 // Copy rest part of current data line to new data line
307 memcpy(p_data_line,
308 p_editor_data->p_display_lines[display_line] + offset,
309 (size_t)(len_data_line - offset_data_line));
310
311 p_data_line[len_data_line - offset_data_line] = '\0';
312
313 // Append str to current display line
314 memcpy(p_editor_data->p_display_lines[display_line] + offset, str, (size_t)str_len);
315
316 // Add line ending to current display line (data line)
317 p_editor_data->p_display_lines[display_line][offset + str_len] = '\n';
318 p_editor_data->p_display_lines[display_line][offset + str_len + 1] = '\0';
319 p_editor_data->display_line_lengths[display_line] = offset + str_len + 1;
320
321 *p_display_line = display_line;
322 *p_offset = offset + str_len;
323 }
324
325 // Update display width of current display line
326 len = split_line(p_editor_data->p_display_lines[display_line], SCREEN_COLS, &eol, &display_len, 0);
327 p_editor_data->display_line_widths[display_line] = display_len;
328
329 split_line_total = last_display_line - display_line + 3;
330
331 // Set start display_line for spliting new data line
332 display_line++;
333
334 *p_last_updated_line = p_editor_data->display_line_total;
335 }
336 else // insert str into current data line at offset_data_line
337 {
338 memmove(p_data_line + offset_data_line + str_len, p_data_line + offset_data_line, (size_t)(len_data_line - offset_data_line));
339 memcpy(p_data_line + offset_data_line, str, (size_t)str_len);
340 p_data_line[len_data_line + str_len] = '\0';
341
342 // Set p_data_line to head of current display line
343 p_data_line = p_editor_data->p_display_lines[display_line];
344 split_line_total = last_display_line - display_line + 3;
345
346 *p_display_line = display_line;
347 *p_offset = offset + str_len;
348 }
349
350 // Split current data line since beginning of current display line
351 split_line_total = split_data_lines(p_data_line, SCREEN_COLS, line_offsets, split_line_total, 0, line_widths);
352
353 for (i = 0; i < split_line_total; i++)
354 {
355 if (display_line + i > last_display_line)
356 {
357 // Insert blank display line after last_display_line
358 if (p_editor_data->display_line_total >= MAX_EDITOR_DATA_LINES)
359 {
360 #ifdef _DEBUG
361 log_error("display_line_total over limit %d >= %d\n", p_editor_data->display_line_total, MAX_EDITOR_DATA_LINES);
362 #endif
363
364 // Terminate prior display line with \n, to avoid error on cleanup
365 if (display_line + i - 1 >= 0 && p_editor_data->display_line_lengths[display_line + i - 1] > 0)
366 {
367 len = split_line(p_editor_data->p_display_lines[display_line + i - 1], SCREEN_COLS - 1, &eol, &display_len, 0);
368 p_editor_data->p_display_lines[display_line + i - 1][len] = '\n';
369 p_editor_data->p_display_lines[display_line + i - 1][len + 1] = '\0';
370 p_editor_data->display_line_lengths[display_line + i - 1] = len + 1;
371 p_editor_data->display_line_widths[display_line + i - 1] = display_len;
372 }
373 if (*p_offset >= p_editor_data->display_line_lengths[*p_display_line])
374 {
375 *p_offset = p_editor_data->display_line_lengths[*p_display_line] - 1;
376 }
377 break;
378 }
379
380 // for (j = p_editor_data->display_line_total; j > last_display_line + 1; j--)
381 // {
382 // p_editor_data->p_display_lines[j] = p_editor_data->p_display_lines[j - 1];
383 // p_editor_data->display_line_lengths[j] = p_editor_data->display_line_lengths[j - 1];
384 // p_editor_data->display_line_widths[j] = p_editor_data->display_line_widths[j - 1];
385 // }
386 memmove(p_editor_data->p_display_lines + last_display_line + 2,
387 p_editor_data->p_display_lines + last_display_line + 1,
388 (size_t)(p_editor_data->display_line_total - last_display_line - 1) *
389 sizeof(p_editor_data->p_display_lines[last_display_line + 1]));
390 memmove(p_editor_data->display_line_lengths + last_display_line + 2,
391 p_editor_data->display_line_lengths + last_display_line + 1,
392 (size_t)(p_editor_data->display_line_total - last_display_line - 1) *
393 sizeof(p_editor_data->display_line_lengths[last_display_line + 1]));
394 memmove(p_editor_data->display_line_widths + last_display_line + 2,
395 p_editor_data->display_line_widths + last_display_line + 1,
396 (size_t)(p_editor_data->display_line_total - last_display_line - 1) *
397 sizeof(p_editor_data->display_line_widths[last_display_line + 1]));
398
399 last_display_line++;
400 *p_last_updated_line = p_editor_data->display_line_total;
401 (p_editor_data->display_line_total)++;
402 }
403
404 p_editor_data->display_line_lengths[display_line + i] = line_offsets[i + 1] - line_offsets[i];
405 p_editor_data->display_line_widths[display_line + i] = line_widths[i];
406 p_editor_data->p_display_lines[display_line + i] =
407 (i == 0
408 ? p_data_line
409 : (p_editor_data->p_display_lines[display_line + i - 1] + p_editor_data->display_line_lengths[display_line + i - 1]));
410
411 if (p_editor_data->display_line_lengths[display_line + i] > 0 &&
412 p_editor_data->p_display_lines[display_line + i][p_editor_data->display_line_lengths[display_line + i] - 1] == '\n')
413 {
414 break;
415 }
416 }
417
418 *p_last_updated_line = MAX(display_line + MIN(i, split_line_total - 1), *p_last_updated_line);
419
420 if (*p_offset >= p_editor_data->display_line_lengths[*p_display_line])
421 {
422 if (*p_display_line + 1 < p_editor_data->display_line_total)
423 {
424 *p_offset -= p_editor_data->display_line_lengths[*p_display_line];
425 (*p_display_line)++;
426 }
427 }
428
429 // Prevent the last display line from being over-length
430 if (p_editor_data->display_line_total == MAX_EDITOR_DATA_LINES)
431 {
432 len = split_line(p_editor_data->p_display_lines[p_editor_data->display_line_total - 1], SCREEN_COLS - 1, &eol, &display_len, 0);
433 p_editor_data->p_display_lines[p_editor_data->display_line_total - 1][len] = '\0';
434 p_editor_data->display_line_lengths[p_editor_data->display_line_total - 1] = len;
435 p_editor_data->display_line_widths[p_editor_data->display_line_total - 1] = display_len;
436 if (*p_display_line + 1 >= p_editor_data->display_line_total)
437 {
438 *p_offset = MIN(*p_offset, len);
439 *p_display_line = p_editor_data->display_line_total - 1;
440 }
441 }
442
443 return 0;
444 }
445
446 int editor_data_delete(EDITOR_DATA *p_editor_data, long *p_display_line, long *p_offset,
447 long *p_last_updated_line)
448 {
449 long display_line = *p_display_line;
450 long offset = *p_offset;
451 char *p_data_line = NULL;
452 long len_data_line;
453 long offset_data_line;
454 long last_display_line; // of data line
455 long line_offsets[MAX_EDITOR_DATA_LINE_LENGTH + 1];
456 int line_widths[MAX_EDITOR_DATA_LINE_LENGTH + 1];
457 long split_line_total;
458 long i, j;
459 int str_len = 0;
460 char c;
461
462 if (p_editor_data == NULL || p_last_updated_line == NULL)
463 {
464 log_error("editor_data_op() error: NULL pointer\n");
465 return -1;
466 }
467
468 // Get length of current data line
469 len_data_line = 0;
470 p_data_line = p_editor_data->p_display_lines[display_line];
471 for (i = display_line - 1; i >= 0; i--)
472 {
473 if (p_editor_data->display_line_lengths[i] > 0 &&
474 p_editor_data->p_display_lines[i][p_editor_data->display_line_lengths[i] - 1] == '\n') // reach end of prior data line
475 {
476 break;
477 }
478
479 len_data_line += p_editor_data->display_line_lengths[i];
480 p_data_line = p_editor_data->p_display_lines[i];
481 }
482 offset_data_line = len_data_line + offset;
483 last_display_line = p_editor_data->display_line_total - 1;
484 for (i = display_line; i < p_editor_data->display_line_total; i++)
485 {
486 len_data_line += p_editor_data->display_line_lengths[i];
487
488 if (p_editor_data->display_line_lengths[i] > 0 &&
489 p_editor_data->p_display_lines[i][p_editor_data->display_line_lengths[i] - 1] == '\n') // reach end of current data line
490 {
491 last_display_line = i;
492 break;
493 }
494 }
495
496 if (offset_data_line >= len_data_line) // end-of-line
497 {
498 return 0;
499 }
500
501 // Check str to be deleted
502 if (p_data_line[offset_data_line] > 0 && p_data_line[offset_data_line] < 127)
503 {
504 str_len = 1;
505 }
506 else if (p_data_line[offset_data_line] & 0b10000000) // head of multi-byte character
507 {
508 str_len = 1;
509 c = (p_data_line[offset_data_line] & 0b01110000) << 1;
510 while (c & 0b10000000)
511 {
512 str_len++;
513 c = (c & 0b01111111) << 1;
514 }
515 }
516 else
517 {
518 log_error("Some strange character at display_line %ld, offset %ld: %d %d\n",
519 display_line, offset, p_data_line[offset_data_line], p_data_line[offset_data_line + 1]);
520 str_len = 1;
521 }
522
523 // Current display line is (almost) empty
524 if (offset_data_line + str_len > len_data_line ||
525 (offset_data_line + str_len == len_data_line && p_data_line[offset_data_line] == '\n'))
526 {
527 if (display_line + 1 >= p_editor_data->display_line_total) // No additional display line (data line)
528 {
529 return 0;
530 }
531
532 len_data_line = 0; // Next data line
533 last_display_line = p_editor_data->display_line_total - 1;
534 for (i = display_line + 1; i < p_editor_data->display_line_total; i++)
535 {
536 len_data_line += p_editor_data->display_line_lengths[i];
537
538 if (p_editor_data->display_line_lengths[i] > 0 &&
539 p_editor_data->p_display_lines[i][p_editor_data->display_line_lengths[i] - 1] == '\n') // reach end of current data line
540 {
541 last_display_line = i;
542 break;
543 }
544 }
545
546 if (offset_data_line + len_data_line + 1 > MAX_EDITOR_DATA_LINE_LENGTH) // No enough buffer to merge current data line with next data line
547 {
548 return 0;
549 }
550
551 // Append next data line to current one
552 memcpy(p_data_line + offset_data_line, p_editor_data->p_display_lines[display_line + 1], (size_t)len_data_line);
553 p_data_line[offset_data_line + len_data_line] = '\0';
554
555 // Recycle next data line
556 memory_pool_free(p_mp_data_line, p_editor_data->p_display_lines[display_line + 1]);
557 }
558 else
559 {
560 memmove(p_data_line + offset_data_line, p_data_line + offset_data_line + str_len, (size_t)(len_data_line - offset_data_line - str_len));
561 p_data_line[len_data_line - str_len] = '\0';
562 len_data_line -= str_len;
563 }
564
565 // Set p_data_line to head of current display line
566 p_data_line = p_editor_data->p_display_lines[display_line];
567 split_line_total = last_display_line - display_line + 2;
568
569 // Split current data line since beginning of current display line
570 split_line_total = split_data_lines(p_data_line, SCREEN_COLS, line_offsets, split_line_total, 0, line_widths);
571
572 for (i = 0; i < split_line_total; i++)
573 {
574 p_editor_data->display_line_lengths[display_line + i] = line_offsets[i + 1] - line_offsets[i];
575 p_editor_data->display_line_widths[display_line + i] = line_widths[i];
576 p_editor_data->p_display_lines[display_line + i] =
577 (i == 0
578 ? p_data_line
579 : (p_editor_data->p_display_lines[display_line + i - 1] + p_editor_data->display_line_lengths[display_line + i - 1]));
580
581 if (p_editor_data->display_line_lengths[display_line + i] > 0 &&
582 p_editor_data->p_display_lines[display_line + i][p_editor_data->display_line_lengths[display_line + i] - 1] == '\n')
583 {
584 break;
585 }
586 }
587
588 *p_last_updated_line = display_line + MIN(i, split_line_total - 1);
589
590 if (*p_last_updated_line < last_display_line)
591 {
592 // Remove redundant display line after last_display_line
593 // for (j = last_display_line + 1; j < p_editor_data->display_line_total; j++)
594 // {
595 // p_editor_data->p_display_lines[j - (last_display_line - *p_last_updated_line)] = p_editor_data->p_display_lines[j];
596 // p_editor_data->display_line_lengths[j - (last_display_line - *p_last_updated_line)] = p_editor_data->display_line_lengths[j];
597 // p_editor_data->display_line_widths[j - (last_display_line - *p_last_updated_line)] = p_editor_data->display_line_widths[j];
598 // }
599 memmove(p_editor_data->p_display_lines + *p_last_updated_line + 1,
600 p_editor_data->p_display_lines + last_display_line + 1,
601 (size_t)(p_editor_data->display_line_total - last_display_line - 1) *
602 sizeof(p_editor_data->p_display_lines[last_display_line + 1]));
603 memmove(p_editor_data->display_line_lengths + *p_last_updated_line + 1,
604 p_editor_data->display_line_lengths + last_display_line + 1,
605 (size_t)(p_editor_data->display_line_total - last_display_line - 1) *
606 sizeof(p_editor_data->display_line_lengths[last_display_line + 1]));
607 memmove(p_editor_data->display_line_widths + *p_last_updated_line + 1,
608 p_editor_data->display_line_widths + last_display_line + 1,
609 (size_t)(p_editor_data->display_line_total - last_display_line - 1) *
610 sizeof(p_editor_data->display_line_widths[last_display_line + 1]));
611
612 j = p_editor_data->display_line_total;
613 (p_editor_data->display_line_total) -= (last_display_line - *p_last_updated_line);
614 *p_last_updated_line = MAX(j - 1, *p_last_updated_line);
615 }
616
617 // Return real offset
618 *p_offset = offset;
619
620 return str_len;
621 }
622
623 static int editor_display_key_handler(int *p_key, EDITOR_CTX *p_ctx)
624 {
625 switch (*p_key)
626 {
627 case 0: // Set msg
628 snprintf(p_ctx->msg, sizeof(p_ctx->msg),
629 "| 退出[\033[32mCtrl-W\033[33m] |");
630 break;
631 case KEY_CSI:
632 *p_key = KEY_ESC;
633 break;
634 }
635
636 return 0;
637 }
638
639 int editor_display(EDITOR_DATA *p_editor_data)
640 {
641 static int show_help = 1;
642 char buffer[MAX_EDITOR_DATA_LINE_LENGTH];
643 EDITOR_CTX ctx;
644 int ch = 0;
645 char input_str[4];
646 char c;
647 int str_len = 0;
648 int input_ok;
649 const int screen_begin_row = 1;
650 const int screen_row_total = SCREEN_ROWS - screen_begin_row;
651 int output_current_row = screen_begin_row;
652 int output_end_row = SCREEN_ROWS - 1;
653 long int line_current = 0;
654 long int len;
655 int loop;
656 int eol, display_len;
657 long row_pos = 1, col_pos = 1;
658 long display_line_in, offset_in;
659 long display_line_out, offset_out;
660 int scroll_rows;
661 long last_updated_line = 0;
662 int key_insert = 1;
663 int i, j;
664 char *p_str;
665
666 clrline(output_current_row, SCREEN_ROWS);
667
668 // update msg_ext with extended key handler
669 if (editor_display_key_handler(&ch, &ctx) != 0)
670 {
671 return ch;
672 }
673
674 for (loop = 1; !SYS_server_exit && loop;)
675 {
676 if (line_current >= p_editor_data->display_line_total || output_current_row > output_end_row)
677 {
678 ctx.line_cursor = line_current - output_current_row + row_pos + 1;
679
680 snprintf(buffer, sizeof(buffer),
681 "\033[1;44;33m[\033[32m%ld\033[33m;\033[32m%ld\033[33m] "
682 "第\033[32m%ld\033[33m/\033[32m%ld\033[33m行 [\033[32m%s\033[33m] "
683 "%s",
684 row_pos, col_pos,
685 ctx.line_cursor, p_editor_data->display_line_total,
686 key_insert ? "插入" : "替换",
687 ctx.msg);
688
689 len = split_line(buffer, SCREEN_COLS, &eol, &display_len, 1);
690 for (; display_len < SCREEN_COLS; display_len++)
691 {
692 buffer[len++] = ' ';
693 }
694 buffer[len] = '\0';
695 strncat(buffer, "\033[m", sizeof(buffer) - 1 - strnlen(buffer, sizeof(buffer)));
696
697 moveto(SCREEN_ROWS, 0);
698 prints("%s", buffer);
699
700 moveto((int)row_pos, (int)col_pos);
701 iflush();
702
703 str_len = 0;
704 ch = igetch_t(MAX_DELAY_TIME);
705 while (!SYS_server_exit)
706 {
707 // extended key handler
708 if (editor_display_key_handler(&ch, &ctx) != 0)
709 {
710 goto cleanup;
711 }
712
713 if (ch < 256 && (ch & 0b10000000)) // head of multi-byte character
714 {
715 str_len = 0;
716 c = (char)(ch & 0b11110000);
717 while (c & 0b10000000)
718 {
719 input_str[str_len] = (char)(ch - 256);
720 str_len++;
721 c = (c & 0b01111111) << 1;
722
723 if ((c & 0b10000000) == 0) // Input completed
724 {
725 break;
726 }
727
728 // Expect additional bytes of input
729 ch = igetch(100); // 0.1 second
730 if (ch == KEY_NULL || ch == KEY_TIMEOUT) // Ignore received bytes if no futher input
731 {
732 #ifdef _DEBUG
733 log_error("Ignore %d bytes of incomplete UTF8 character\n", str_len);
734 #endif
735 str_len = 0;
736 break;
737 }
738 }
739 }
740
741 if ((ch >= 32 && ch < 127) || str_len >= 2 || // Printable character or multi-byte character
742 ch == CR || ch == KEY_ESC) // Special character
743 {
744 BBS_last_access_tm = time(NULL);
745
746 if (str_len == 0) // ch >= 32 && ch < 127
747 {
748 input_str[0] = (char)ch;
749 str_len = 1;
750 }
751
752 display_line_in = line_current - output_current_row + row_pos;
753 offset_in = split_line(p_editor_data->p_display_lines[display_line_in], (int)col_pos - 1, &eol, &display_len, 0);
754 display_line_out = display_line_in;
755 offset_out = offset_in;
756
757 last_updated_line = display_line_in;
758
759 if (!key_insert) // overwrite
760 {
761 if (editor_data_delete(p_editor_data, &display_line_out, &offset_out,
762 &last_updated_line) < 0)
763 {
764 log_error("editor_data_delete() error\n");
765 }
766 }
767
768 if (editor_data_insert(p_editor_data, &display_line_out, &offset_out,
769 input_str, str_len, &last_updated_line) < 0)
770 {
771 log_error("editor_data_insert(str_len=%d) error\n", str_len);
772 }
773 else
774 {
775 output_end_row = MIN(SCREEN_ROWS - 1, output_current_row + (int)(last_updated_line - line_current));
776 line_current -= (output_current_row - row_pos);
777 output_current_row = (int)row_pos;
778
779 scroll_rows = MAX(0, (int)(display_line_out - display_line_in) - (output_end_row - output_current_row));
780
781 if (scroll_rows > 0)
782 {
783 moveto(SCREEN_ROWS, 0);
784 clrtoeol();
785 for (i = 0; i < scroll_rows; i++)
786 {
787 // prints("\033[S"); // Scroll up 1 line
788 prints("\n"); // Legacy Cterm only works with this line
789 }
790
791 output_current_row -= scroll_rows;
792 if (output_current_row < screen_begin_row)
793 {
794 line_current += (screen_begin_row - output_current_row);
795 output_current_row = screen_begin_row;
796 }
797 row_pos = output_end_row;
798 }
799 else // if (scroll_lines == 0)
800 {
801 row_pos += (display_line_out - display_line_in);
802 }
803
804 if (offset_out != offset_in)
805 {
806 if (display_line_out != display_line_in)
807 {
808 col_pos = 1;
809 }
810 if (offset_out > 0)
811 {
812 col_pos += (str_len == 1 ? 1 : 2);
813 }
814 }
815 }
816
817 if (display_line_out != display_line_in) // Output on line change
818 {
819 break;
820 }
821
822 ch = igetch(0);
823 if (ch == KEY_NULL || ch == KEY_TIMEOUT) // Output if no futher input
824 {
825 break;
826 }
827
828 str_len = 0;
829 continue;
830 }
831 else if (ch == KEY_DEL || ch == BACKSPACE) // Del
832 {
833 BBS_last_access_tm = time(NULL);
834
835 if (ch == BACKSPACE)
836 {
837 if (line_current - output_current_row + row_pos <= 0 && col_pos <= 1) // Forbidden
838 {
839 break; // force output prior operation result if any
840 }
841
842 offset_in = split_line(p_editor_data->p_display_lines[line_current - output_current_row + row_pos],
843 (int)col_pos - 1, &eol, &display_len, 0);
844 if (offset_in >= 1 && p_editor_data->p_display_lines[line_current - output_current_row + row_pos][offset_in - 1] < 0) // UTF8
845 {
846 col_pos = display_len - 1;
847 }
848 else
849 {
850 col_pos = display_len;
851 }
852
853 if (col_pos < 1 && line_current - output_current_row + row_pos >= 0)
854 {
855 row_pos--;
856 col_pos = MAX(1, p_editor_data->display_line_widths[line_current - output_current_row + row_pos]);
857 }
858 }
859
860 display_line_in = line_current - output_current_row + row_pos;
861 offset_in = split_line(p_editor_data->p_display_lines[display_line_in], (int)col_pos - 1, &eol, &display_len, 0);
862 display_line_out = display_line_in;
863 offset_out = offset_in;
864
865 if ((str_len = editor_data_delete(p_editor_data, &display_line_out, &offset_out,
866 &last_updated_line)) < 0)
867 {
868 log_error("editor_data_delete() error\n");
869 }
870 else
871 {
872 col_pos = display_len + 1; // Set col_pos to accurate pos
873
874 output_end_row = MIN(SCREEN_ROWS - 1, output_current_row + (int)(last_updated_line - line_current));
875 line_current -= (output_current_row - row_pos);
876 output_current_row = (int)row_pos;
877
878 if (output_current_row < screen_begin_row) // row_pos <= 0
879 {
880 output_current_row = screen_begin_row;
881 row_pos = screen_begin_row;
882 output_end_row = SCREEN_ROWS - 1;
883 }
884
885 // Exceed end
886 if (line_current + (screen_row_total - output_current_row) >= p_editor_data->display_line_total &&
887 p_editor_data->display_line_total > screen_row_total)
888 {
889 scroll_rows = (int)((line_current - (output_current_row - screen_begin_row)) -
890 (p_editor_data->display_line_total - screen_row_total));
891
892 line_current = p_editor_data->display_line_total - screen_row_total;
893 row_pos += scroll_rows;
894 output_current_row = screen_begin_row;
895 output_end_row = SCREEN_ROWS - 1;
896 }
897
898 clrline(output_current_row, output_end_row);
899 }
900
901 if (display_line_out != display_line_in) // Output on line change
902 {
903 break;
904 }
905
906 ch = igetch(0);
907 if (ch == KEY_NULL || ch == KEY_TIMEOUT) // Output if no futher input
908 {
909 break;
910 }
911
912 str_len = 0;
913 continue;
914 }
915
916 input_ok = 1;
917 switch (ch)
918 {
919 case KEY_NULL:
920 case KEY_TIMEOUT:
921 goto cleanup;
922 case Ctrl('W'):
923 case Ctrl('X'):
924 loop = 0;
925 break;
926 case Ctrl('S'): // Start of line
927 case KEY_CTRL_LEFT:
928 col_pos = 1;
929 break;
930 case Ctrl('E'): // End of line
931 case KEY_CTRL_RIGHT:
932 if (line_current - output_current_row + row_pos == p_editor_data->display_line_total - 1) // row_pos at end line
933 {
934 // last display line does NOT have \n in the end
935 col_pos = p_editor_data->display_line_widths[line_current - output_current_row + row_pos] + 1;
936 break;
937 }
938 col_pos = MAX(1, p_editor_data->display_line_widths[line_current - output_current_row + row_pos]);
939 break;
940 case Ctrl('T'): // Top of screen
941 case KEY_CTRL_UP:
942 row_pos = screen_begin_row;
943 col_pos = MIN(col_pos, MAX(1, p_editor_data->display_line_widths[line_current - output_current_row + row_pos]));
944 break;
945 case Ctrl('B'): // Bottom of screen
946 case KEY_CTRL_DOWN:
947 if (p_editor_data->display_line_total < screen_row_total)
948 {
949 row_pos = p_editor_data->display_line_total;
950 }
951 else
952 {
953 row_pos = SCREEN_ROWS - 1;
954 }
955 if (line_current + (screen_row_total - (output_current_row - screen_begin_row)) >= p_editor_data->display_line_total) // Reach end
956 {
957 // last display line does NOT have \n in the end
958 col_pos = MIN(col_pos, p_editor_data->display_line_widths[line_current - output_current_row + row_pos] + 1);
959 }
960 else
961 {
962 col_pos = MIN(col_pos, MAX(1, p_editor_data->display_line_widths[line_current - output_current_row + row_pos]));
963 }
964 break;
965 case KEY_INS:
966 key_insert = !key_insert;
967 break;
968 case KEY_HOME:
969 row_pos = 1;
970 col_pos = 1;
971 if (line_current - output_current_row < 0) // Reach begin
972 {
973 break;
974 }
975 line_current = 0;
976 output_current_row = screen_begin_row;
977 output_end_row = SCREEN_ROWS - 1;
978 clrline(output_current_row, SCREEN_ROWS);
979 break;
980 case KEY_END:
981 if (p_editor_data->display_line_total < screen_row_total)
982 {
983 row_pos = p_editor_data->display_line_total;
984 col_pos = p_editor_data->display_line_widths[line_current - output_current_row + row_pos] + 1;
985 break;
986 }
987 line_current = p_editor_data->display_line_total - screen_row_total;
988 output_current_row = screen_begin_row;
989 output_end_row = SCREEN_ROWS - 1;
990 row_pos = SCREEN_ROWS - 1;
991 col_pos = p_editor_data->display_line_widths[line_current - output_current_row + row_pos] + 1;
992 clrline(output_current_row, SCREEN_ROWS);
993 break;
994 case KEY_LEFT:
995 offset_in = split_line(p_editor_data->p_display_lines[line_current - output_current_row + row_pos],
996 (int)col_pos - 1, &eol, &display_len, 0);
997 if (offset_in >= 1 && p_editor_data->p_display_lines[line_current - output_current_row + row_pos][offset_in - 1] < 0) // UTF8
998 {
999 col_pos = display_len - 1;
1000 }
1001 else
1002 {
1003 col_pos = display_len;
1004 }
1005 if (col_pos >= 1)
1006 {
1007 break;
1008 }
1009 col_pos = SCREEN_COLS; // continue to KEY_UP
1010 case KEY_UP:
1011 if (row_pos > screen_begin_row)
1012 {
1013 row_pos--;
1014 col_pos = MIN(col_pos, MAX(1, p_editor_data->display_line_widths[line_current - output_current_row + row_pos]));
1015 break;
1016 }
1017 if (line_current - output_current_row < 0) // Reach begin
1018 {
1019 col_pos = 1;
1020 break;
1021 }
1022 line_current -= output_current_row;
1023 output_current_row = screen_begin_row;
1024 // screen_end_line = begin_line;
1025 // prints("\033[T"); // Scroll down 1 line
1026 output_end_row = SCREEN_ROWS - 1; // Legacy Fterm only works with this line
1027 col_pos = MIN(col_pos, MAX(1, p_editor_data->display_line_widths[line_current - output_current_row + row_pos]));
1028 break;
1029 case KEY_SPACE:
1030 break;
1031 case KEY_RIGHT:
1032 offset_in = split_line(p_editor_data->p_display_lines[line_current - output_current_row + row_pos],
1033 (int)col_pos - 1, &eol, &display_len, 0);
1034 if (offset_in < p_editor_data->display_line_lengths[line_current - output_current_row + row_pos] &&
1035 p_editor_data->p_display_lines[line_current - output_current_row + row_pos][offset_in] < 0) // UTF8
1036 {
1037 col_pos = display_len + 3;
1038 }
1039 else
1040 {
1041 col_pos = display_len + 2;
1042 }
1043 if (col_pos <= p_editor_data->display_line_widths[line_current - output_current_row + row_pos])
1044 {
1045 break;
1046 }
1047 col_pos = 1; // continue to KEY_DOWN
1048 case KEY_DOWN:
1049 if (row_pos < MIN(screen_row_total, p_editor_data->display_line_total))
1050 {
1051 row_pos++;
1052 col_pos = MIN(col_pos, MAX(1, p_editor_data->display_line_widths[line_current - output_current_row + row_pos]));
1053 break;
1054 }
1055 if (line_current - output_current_row + row_pos == p_editor_data->display_line_total - 1) // row_pos at end line
1056 {
1057 // last display line does NOT have \n in the end
1058 col_pos = p_editor_data->display_line_widths[line_current - output_current_row + row_pos] + 1;
1059 break;
1060 }
1061 line_current += (screen_row_total - (output_current_row - screen_begin_row));
1062 output_current_row = screen_row_total;
1063 output_end_row = SCREEN_ROWS - 1;
1064 col_pos = MIN(col_pos, MAX(1, p_editor_data->display_line_widths[line_current - output_current_row + row_pos]));
1065 moveto(SCREEN_ROWS, 0);
1066 clrtoeol();
1067 // prints("\033[S"); // Scroll up 1 line
1068 prints("\n"); // Legacy Cterm only works with this line
1069 break;
1070 case KEY_PGUP:
1071 if (line_current - output_current_row < 0) // Reach begin
1072 {
1073 break;
1074 }
1075 line_current -= ((screen_row_total - 1) + (output_current_row - screen_begin_row));
1076 if (line_current < 0)
1077 {
1078 line_current = 0;
1079 }
1080 output_current_row = screen_begin_row;
1081 output_end_row = SCREEN_ROWS - 1;
1082 col_pos = MIN(col_pos, MAX(1, p_editor_data->display_line_widths[line_current - output_current_row + row_pos]));
1083 clrline(output_current_row, SCREEN_ROWS);
1084 break;
1085 case KEY_PGDN:
1086 if (line_current + screen_row_total - (output_current_row - screen_begin_row) >= p_editor_data->display_line_total) // Reach end
1087 {
1088 break;
1089 }
1090 line_current += (screen_row_total - 1) - (output_current_row - screen_begin_row);
1091 if (line_current + screen_row_total > p_editor_data->display_line_total) // No enough lines to display
1092 {
1093 line_current = p_editor_data->display_line_total - screen_row_total;
1094 }
1095 output_current_row = screen_begin_row;
1096 output_end_row = SCREEN_ROWS - 1;
1097 col_pos = MIN(col_pos, MAX(1, p_editor_data->display_line_widths[line_current - output_current_row + row_pos]));
1098 clrline(output_current_row, SCREEN_ROWS);
1099 break;
1100 case KEY_F1:
1101 if (!show_help) // Not reentrant
1102 {
1103 break;
1104 }
1105 // Display help information
1106 show_help = 0;
1107 display_file(DATA_READ_HELP, 1);
1108 show_help = 1;
1109 case KEY_F5:
1110 // Refresh after display help information
1111 line_current -= (output_current_row - screen_begin_row);
1112 output_current_row = screen_begin_row;
1113 output_end_row = SCREEN_ROWS - 1;
1114 clrline(output_current_row, SCREEN_ROWS);
1115 break;
1116 case 0: // Refresh bottom line
1117 break;
1118 default:
1119 input_ok = 0;
1120 break;
1121 }
1122
1123 BBS_last_access_tm = time(NULL);
1124
1125 if (input_ok)
1126 {
1127 break;
1128 }
1129
1130 ch = igetch_t(MAX_DELAY_TIME);
1131 }
1132
1133 continue;
1134 }
1135
1136 len = p_editor_data->display_line_lengths[line_current];
1137 if (len >= sizeof(buffer))
1138 {
1139 log_error("Buffer overflow: len=%ld line=%ld \n", len, line_current);
1140 len = sizeof(buffer) - 1;
1141 }
1142 else if (len < 0)
1143 {
1144 log_error("Incorrect line offsets: len=%ld line=%ld \n", len, line_current);
1145 len = 0;
1146 }
1147
1148 // memcpy(buffer, p_editor_data->p_display_lines[line_current], (size_t)len);
1149 // Replace '\033' with '*'
1150 p_str = p_editor_data->p_display_lines[line_current];
1151 for (i = 0, j = 0; i < len; i++)
1152 {
1153 if (p_str[i] == '\033')
1154 {
1155 memcpy(buffer + j, EDITOR_ESC_DISPLAY_STR, sizeof(EDITOR_ESC_DISPLAY_STR) - 1);
1156 j += (int)(sizeof(EDITOR_ESC_DISPLAY_STR) - 1);
1157 }
1158 else
1159 {
1160 buffer[j] = p_str[i];
1161 j++;
1162 }
1163 }
1164 buffer[j] = '\0';
1165
1166 moveto(output_current_row, 0);
1167 clrtoeol();
1168 prints("%s", buffer);
1169 line_current++;
1170 output_current_row++;
1171 }
1172
1173 cleanup:
1174 return ch;
1175 }

webmaster@leafok.com
ViewVC Help
Powered by ViewVC 1.3.0-beta1