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

Contents of /lbbs/src/editor.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.40 - (show annotations)
Sun Jul 20 02:04:21 2025 UTC (7 months, 3 weeks ago) by sysadm
Branch: MAIN
Changes since 1.39: +2 -0 lines
Content type: text/x-csrc
Logging incorrent UTF8 input only when _DEBUG is defined

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 (ch != CR)
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 loop = 0;
924 break;
925 case Ctrl('S'): // Start of line
926 case KEY_CTRL_LEFT:
927 col_pos = 1;
928 break;
929 case Ctrl('E'): // End of line
930 case KEY_CTRL_RIGHT:
931 if (line_current - output_current_row + row_pos == p_editor_data->display_line_total - 1) // row_pos at end line
932 {
933 // last display line does NOT have \n in the end
934 col_pos = p_editor_data->display_line_widths[line_current - output_current_row + row_pos] + 1;
935 break;
936 }
937 col_pos = MAX(1, p_editor_data->display_line_widths[line_current - output_current_row + row_pos]);
938 break;
939 case Ctrl('T'): // Top of screen
940 case KEY_CTRL_UP:
941 row_pos = screen_begin_row;
942 col_pos = MIN(col_pos, MAX(1, p_editor_data->display_line_widths[line_current - output_current_row + row_pos]));
943 break;
944 case Ctrl('B'): // Bottom of screen
945 case KEY_CTRL_DOWN:
946 if (p_editor_data->display_line_total < screen_row_total)
947 {
948 row_pos = p_editor_data->display_line_total;
949 }
950 else
951 {
952 row_pos = SCREEN_ROWS - 1;
953 }
954 if (line_current + (screen_row_total - (output_current_row - screen_begin_row)) >= p_editor_data->display_line_total) // Reach end
955 {
956 // last display line does NOT have \n in the end
957 col_pos = MIN(col_pos, p_editor_data->display_line_widths[line_current - output_current_row + row_pos] + 1);
958 }
959 else
960 {
961 col_pos = MIN(col_pos, MAX(1, p_editor_data->display_line_widths[line_current - output_current_row + row_pos]));
962 }
963 break;
964 case KEY_INS:
965 key_insert = !key_insert;
966 break;
967 case KEY_HOME:
968 row_pos = 1;
969 col_pos = 1;
970 if (line_current - output_current_row < 0) // Reach begin
971 {
972 break;
973 }
974 line_current = 0;
975 output_current_row = screen_begin_row;
976 output_end_row = SCREEN_ROWS - 1;
977 clrline(output_current_row, SCREEN_ROWS);
978 break;
979 case KEY_END:
980 if (p_editor_data->display_line_total < screen_row_total)
981 {
982 row_pos = p_editor_data->display_line_total;
983 col_pos = p_editor_data->display_line_widths[line_current - output_current_row + row_pos] + 1;
984 break;
985 }
986 line_current = p_editor_data->display_line_total - screen_row_total;
987 output_current_row = screen_begin_row;
988 output_end_row = SCREEN_ROWS - 1;
989 row_pos = SCREEN_ROWS - 1;
990 col_pos = p_editor_data->display_line_widths[line_current - output_current_row + row_pos] + 1;
991 clrline(output_current_row, SCREEN_ROWS);
992 break;
993 case KEY_LEFT:
994 offset_in = split_line(p_editor_data->p_display_lines[line_current - output_current_row + row_pos],
995 (int)col_pos - 1, &eol, &display_len, 0);
996 if (offset_in >= 1 && p_editor_data->p_display_lines[line_current - output_current_row + row_pos][offset_in - 1] < 0) // UTF8
997 {
998 col_pos = display_len - 1;
999 }
1000 else
1001 {
1002 col_pos = display_len;
1003 }
1004 if (col_pos >= 1)
1005 {
1006 break;
1007 }
1008 col_pos = SCREEN_COLS; // continue to KEY_UP
1009 case KEY_UP:
1010 if (row_pos > screen_begin_row)
1011 {
1012 row_pos--;
1013 col_pos = MIN(col_pos, MAX(1, p_editor_data->display_line_widths[line_current - output_current_row + row_pos]));
1014 break;
1015 }
1016 if (line_current - output_current_row < 0) // Reach begin
1017 {
1018 col_pos = 1;
1019 break;
1020 }
1021 line_current -= output_current_row;
1022 output_current_row = screen_begin_row;
1023 // screen_end_line = begin_line;
1024 // prints("\033[T"); // Scroll down 1 line
1025 output_end_row = SCREEN_ROWS - 1; // Legacy Fterm only works with this line
1026 col_pos = MIN(col_pos, MAX(1, p_editor_data->display_line_widths[line_current - output_current_row + row_pos]));
1027 break;
1028 case KEY_SPACE:
1029 break;
1030 case KEY_RIGHT:
1031 offset_in = split_line(p_editor_data->p_display_lines[line_current - output_current_row + row_pos],
1032 (int)col_pos - 1, &eol, &display_len, 0);
1033 if (offset_in < p_editor_data->display_line_lengths[line_current - output_current_row + row_pos] &&
1034 p_editor_data->p_display_lines[line_current - output_current_row + row_pos][offset_in] < 0) // UTF8
1035 {
1036 col_pos = display_len + 3;
1037 }
1038 else
1039 {
1040 col_pos = display_len + 2;
1041 }
1042 if (col_pos <= p_editor_data->display_line_widths[line_current - output_current_row + row_pos])
1043 {
1044 break;
1045 }
1046 col_pos = 1; // continue to KEY_DOWN
1047 case KEY_DOWN:
1048 if (row_pos < MIN(screen_row_total, p_editor_data->display_line_total))
1049 {
1050 row_pos++;
1051 col_pos = MIN(col_pos, MAX(1, p_editor_data->display_line_widths[line_current - output_current_row + row_pos]));
1052 break;
1053 }
1054 if (line_current - output_current_row + row_pos == p_editor_data->display_line_total - 1) // row_pos at end line
1055 {
1056 // last display line does NOT have \n in the end
1057 col_pos = p_editor_data->display_line_widths[line_current - output_current_row + row_pos] + 1;
1058 break;
1059 }
1060 line_current += (screen_row_total - (output_current_row - screen_begin_row));
1061 output_current_row = screen_row_total;
1062 output_end_row = SCREEN_ROWS - 1;
1063 col_pos = MIN(col_pos, MAX(1, p_editor_data->display_line_widths[line_current - output_current_row + row_pos]));
1064 moveto(SCREEN_ROWS, 0);
1065 clrtoeol();
1066 // prints("\033[S"); // Scroll up 1 line
1067 prints("\n"); // Legacy Cterm only works with this line
1068 break;
1069 case KEY_PGUP:
1070 if (line_current - output_current_row < 0) // Reach begin
1071 {
1072 break;
1073 }
1074 line_current -= ((screen_row_total - 1) + (output_current_row - screen_begin_row));
1075 if (line_current < 0)
1076 {
1077 line_current = 0;
1078 }
1079 output_current_row = screen_begin_row;
1080 output_end_row = SCREEN_ROWS - 1;
1081 col_pos = MIN(col_pos, MAX(1, p_editor_data->display_line_widths[line_current - output_current_row + row_pos]));
1082 clrline(output_current_row, SCREEN_ROWS);
1083 break;
1084 case KEY_PGDN:
1085 if (line_current + screen_row_total - (output_current_row - screen_begin_row) >= p_editor_data->display_line_total) // Reach end
1086 {
1087 break;
1088 }
1089 line_current += (screen_row_total - 1) - (output_current_row - screen_begin_row);
1090 if (line_current + screen_row_total > p_editor_data->display_line_total) // No enough lines to display
1091 {
1092 line_current = p_editor_data->display_line_total - screen_row_total;
1093 }
1094 output_current_row = screen_begin_row;
1095 output_end_row = SCREEN_ROWS - 1;
1096 col_pos = MIN(col_pos, MAX(1, p_editor_data->display_line_widths[line_current - output_current_row + row_pos]));
1097 clrline(output_current_row, SCREEN_ROWS);
1098 break;
1099 case KEY_F1:
1100 if (!show_help) // Not reentrant
1101 {
1102 break;
1103 }
1104 // Display help information
1105 show_help = 0;
1106 display_file(DATA_READ_HELP, 1);
1107 show_help = 1;
1108 case KEY_F5:
1109 // Refresh after display help information
1110 line_current -= (output_current_row - screen_begin_row);
1111 output_current_row = screen_begin_row;
1112 output_end_row = SCREEN_ROWS - 1;
1113 clrline(output_current_row, SCREEN_ROWS);
1114 break;
1115 case 0: // Refresh bottom line
1116 break;
1117 default:
1118 input_ok = 0;
1119 break;
1120 }
1121
1122 BBS_last_access_tm = time(NULL);
1123
1124 if (input_ok)
1125 {
1126 break;
1127 }
1128
1129 ch = igetch_t(MAX_DELAY_TIME);
1130 }
1131
1132 continue;
1133 }
1134
1135 len = p_editor_data->display_line_lengths[line_current];
1136 if (len >= sizeof(buffer))
1137 {
1138 log_error("Buffer overflow: len=%ld line=%ld \n", len, line_current);
1139 len = sizeof(buffer) - 1;
1140 }
1141 else if (len < 0)
1142 {
1143 log_error("Incorrect line offsets: len=%ld line=%ld \n", len, line_current);
1144 len = 0;
1145 }
1146
1147 // memcpy(buffer, p_editor_data->p_display_lines[line_current], (size_t)len);
1148 // Replace '\033' with '*'
1149 p_str = p_editor_data->p_display_lines[line_current];
1150 for (i = 0, j = 0; i < len; i++)
1151 {
1152 if (p_str[i] == '\033')
1153 {
1154 memcpy(buffer + j, EDITOR_ESC_DISPLAY_STR, sizeof(EDITOR_ESC_DISPLAY_STR) - 1);
1155 j += (int)(sizeof(EDITOR_ESC_DISPLAY_STR) - 1);
1156 }
1157 else
1158 {
1159 buffer[j] = p_str[i];
1160 j++;
1161 }
1162 }
1163 buffer[j] = '\0';
1164
1165 moveto(output_current_row, 0);
1166 clrtoeol();
1167 prints("%s", buffer);
1168 line_current++;
1169 output_current_row++;
1170 }
1171
1172 cleanup:
1173 return ch;
1174 }

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