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

Contents of /lbbs/src/editor.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.39 - (show annotations)
Wed Jul 2 04:17:33 2025 UTC (8 months, 2 weeks ago) by sysadm
Branch: MAIN
Changes since 1.38: +123 -89 lines
Content type: text/x-csrc
Support UTF8 instead of GBK

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 log_error("Ignore %d bytes of incomplete UTF8 character\n", str_len);
733 str_len = 0;
734 break;
735 }
736 }
737 }
738
739 if ((ch >= 32 && ch < 127) || str_len >= 2 || // Printable character or multi-byte character
740 ch == CR || ch == KEY_ESC) // Special character
741 {
742 BBS_last_access_tm = time(NULL);
743
744 if (str_len == 0) // ch >= 32 && ch < 127
745 {
746 input_str[0] = (char)ch;
747 str_len = 1;
748 }
749
750 display_line_in = line_current - output_current_row + row_pos;
751 offset_in = split_line(p_editor_data->p_display_lines[display_line_in], (int)col_pos - 1, &eol, &display_len, 0);
752 display_line_out = display_line_in;
753 offset_out = offset_in;
754
755 last_updated_line = display_line_in;
756
757 if (!key_insert) // overwrite
758 {
759 if (editor_data_delete(p_editor_data, &display_line_out, &offset_out,
760 &last_updated_line) < 0)
761 {
762 log_error("editor_data_delete() error\n");
763 }
764 }
765
766 if (editor_data_insert(p_editor_data, &display_line_out, &offset_out,
767 input_str, str_len, &last_updated_line) < 0)
768 {
769 log_error("editor_data_insert(str_len=%d) error\n", str_len);
770 }
771 else
772 {
773 output_end_row = MIN(SCREEN_ROWS - 1, output_current_row + (int)(last_updated_line - line_current));
774 line_current -= (output_current_row - row_pos);
775 output_current_row = (int)row_pos;
776
777 scroll_rows = MAX(0, (int)(display_line_out - display_line_in) - (output_end_row - output_current_row));
778
779 if (scroll_rows > 0)
780 {
781 moveto(SCREEN_ROWS, 0);
782 clrtoeol();
783 for (i = 0; i < scroll_rows; i++)
784 {
785 // prints("\033[S"); // Scroll up 1 line
786 prints("\n"); // Legacy Cterm only works with this line
787 }
788
789 output_current_row -= scroll_rows;
790 if (output_current_row < screen_begin_row)
791 {
792 line_current += (screen_begin_row - output_current_row);
793 output_current_row = screen_begin_row;
794 }
795 row_pos = output_end_row;
796 }
797 else // if (scroll_lines == 0)
798 {
799 row_pos += (display_line_out - display_line_in);
800 }
801
802 if (offset_out != offset_in)
803 {
804 if (display_line_out != display_line_in)
805 {
806 col_pos = 1;
807 }
808 if (ch != CR)
809 {
810 col_pos += (str_len == 1 ? 1 : 2);
811 }
812 }
813 }
814
815 if (display_line_out != display_line_in) // Output on line change
816 {
817 break;
818 }
819
820 ch = igetch(0);
821 if (ch == KEY_NULL || ch == KEY_TIMEOUT) // Output if no futher input
822 {
823 break;
824 }
825
826 str_len = 0;
827 continue;
828 }
829 else if (ch == KEY_DEL || ch == BACKSPACE) // Del
830 {
831 BBS_last_access_tm = time(NULL);
832
833 if (ch == BACKSPACE)
834 {
835 if (line_current - output_current_row + row_pos <= 0 && col_pos <= 1) // Forbidden
836 {
837 break; // force output prior operation result if any
838 }
839
840 offset_in = split_line(p_editor_data->p_display_lines[line_current - output_current_row + row_pos],
841 (int)col_pos - 1, &eol, &display_len, 0);
842 if (offset_in >= 1 && p_editor_data->p_display_lines[line_current - output_current_row + row_pos][offset_in - 1] < 0) // UTF8
843 {
844 col_pos = display_len - 1;
845 }
846 else
847 {
848 col_pos = display_len;
849 }
850
851 if (col_pos < 1 && line_current - output_current_row + row_pos >= 0)
852 {
853 row_pos--;
854 col_pos = MAX(1, p_editor_data->display_line_widths[line_current - output_current_row + row_pos]);
855 }
856 }
857
858 display_line_in = line_current - output_current_row + row_pos;
859 offset_in = split_line(p_editor_data->p_display_lines[display_line_in], (int)col_pos - 1, &eol, &display_len, 0);
860 display_line_out = display_line_in;
861 offset_out = offset_in;
862
863 if ((str_len = editor_data_delete(p_editor_data, &display_line_out, &offset_out,
864 &last_updated_line)) < 0)
865 {
866 log_error("editor_data_delete() error\n");
867 }
868 else
869 {
870 col_pos = display_len + 1; // Set col_pos to accurate pos
871
872 output_end_row = MIN(SCREEN_ROWS - 1, output_current_row + (int)(last_updated_line - line_current));
873 line_current -= (output_current_row - row_pos);
874 output_current_row = (int)row_pos;
875
876 if (output_current_row < screen_begin_row) // row_pos <= 0
877 {
878 output_current_row = screen_begin_row;
879 row_pos = screen_begin_row;
880 output_end_row = SCREEN_ROWS - 1;
881 }
882
883 // Exceed end
884 if (line_current + (screen_row_total - output_current_row) >= p_editor_data->display_line_total &&
885 p_editor_data->display_line_total > screen_row_total)
886 {
887 scroll_rows = (int)((line_current - (output_current_row - screen_begin_row)) -
888 (p_editor_data->display_line_total - screen_row_total));
889
890 line_current = p_editor_data->display_line_total - screen_row_total;
891 row_pos += scroll_rows;
892 output_current_row = screen_begin_row;
893 output_end_row = SCREEN_ROWS - 1;
894 }
895
896 clrline(output_current_row, output_end_row);
897 }
898
899 if (display_line_out != display_line_in) // Output on line change
900 {
901 break;
902 }
903
904 ch = igetch(0);
905 if (ch == KEY_NULL || ch == KEY_TIMEOUT) // Output if no futher input
906 {
907 break;
908 }
909
910 str_len = 0;
911 continue;
912 }
913
914 input_ok = 1;
915 switch (ch)
916 {
917 case KEY_NULL:
918 case KEY_TIMEOUT:
919 goto cleanup;
920 case Ctrl('W'):
921 loop = 0;
922 break;
923 case Ctrl('S'): // Start of line
924 case KEY_CTRL_LEFT:
925 col_pos = 1;
926 break;
927 case Ctrl('E'): // End of line
928 case KEY_CTRL_RIGHT:
929 if (line_current - output_current_row + row_pos == p_editor_data->display_line_total - 1) // row_pos at end line
930 {
931 // last display line does NOT have \n in the end
932 col_pos = p_editor_data->display_line_widths[line_current - output_current_row + row_pos] + 1;
933 break;
934 }
935 col_pos = MAX(1, p_editor_data->display_line_widths[line_current - output_current_row + row_pos]);
936 break;
937 case Ctrl('T'): // Top of screen
938 case KEY_CTRL_UP:
939 row_pos = screen_begin_row;
940 col_pos = MIN(col_pos, MAX(1, p_editor_data->display_line_widths[line_current - output_current_row + row_pos]));
941 break;
942 case Ctrl('B'): // Bottom of screen
943 case KEY_CTRL_DOWN:
944 if (p_editor_data->display_line_total < screen_row_total)
945 {
946 row_pos = p_editor_data->display_line_total;
947 }
948 else
949 {
950 row_pos = SCREEN_ROWS - 1;
951 }
952 if (line_current + (screen_row_total - (output_current_row - screen_begin_row)) >= p_editor_data->display_line_total) // Reach end
953 {
954 // last display line does NOT have \n in the end
955 col_pos = MIN(col_pos, p_editor_data->display_line_widths[line_current - output_current_row + row_pos] + 1);
956 }
957 else
958 {
959 col_pos = MIN(col_pos, MAX(1, p_editor_data->display_line_widths[line_current - output_current_row + row_pos]));
960 }
961 break;
962 case KEY_INS:
963 key_insert = !key_insert;
964 break;
965 case KEY_HOME:
966 row_pos = 1;
967 col_pos = 1;
968 if (line_current - output_current_row < 0) // Reach begin
969 {
970 break;
971 }
972 line_current = 0;
973 output_current_row = screen_begin_row;
974 output_end_row = SCREEN_ROWS - 1;
975 clrline(output_current_row, SCREEN_ROWS);
976 break;
977 case KEY_END:
978 if (p_editor_data->display_line_total < screen_row_total)
979 {
980 row_pos = p_editor_data->display_line_total;
981 col_pos = p_editor_data->display_line_widths[line_current - output_current_row + row_pos] + 1;
982 break;
983 }
984 line_current = p_editor_data->display_line_total - screen_row_total;
985 output_current_row = screen_begin_row;
986 output_end_row = SCREEN_ROWS - 1;
987 row_pos = SCREEN_ROWS - 1;
988 col_pos = p_editor_data->display_line_widths[line_current - output_current_row + row_pos] + 1;
989 clrline(output_current_row, SCREEN_ROWS);
990 break;
991 case KEY_LEFT:
992 offset_in = split_line(p_editor_data->p_display_lines[line_current - output_current_row + row_pos],
993 (int)col_pos - 1, &eol, &display_len, 0);
994 if (offset_in >= 1 && p_editor_data->p_display_lines[line_current - output_current_row + row_pos][offset_in - 1] < 0) // UTF8
995 {
996 col_pos = display_len - 1;
997 }
998 else
999 {
1000 col_pos = display_len;
1001 }
1002 if (col_pos >= 1)
1003 {
1004 break;
1005 }
1006 col_pos = SCREEN_COLS; // continue to KEY_UP
1007 case KEY_UP:
1008 if (row_pos > screen_begin_row)
1009 {
1010 row_pos--;
1011 col_pos = MIN(col_pos, MAX(1, p_editor_data->display_line_widths[line_current - output_current_row + row_pos]));
1012 break;
1013 }
1014 if (line_current - output_current_row < 0) // Reach begin
1015 {
1016 col_pos = 1;
1017 break;
1018 }
1019 line_current -= output_current_row;
1020 output_current_row = screen_begin_row;
1021 // screen_end_line = begin_line;
1022 // prints("\033[T"); // Scroll down 1 line
1023 output_end_row = SCREEN_ROWS - 1; // Legacy Fterm only works with this line
1024 col_pos = MIN(col_pos, MAX(1, p_editor_data->display_line_widths[line_current - output_current_row + row_pos]));
1025 break;
1026 case KEY_SPACE:
1027 break;
1028 case KEY_RIGHT:
1029 offset_in = split_line(p_editor_data->p_display_lines[line_current - output_current_row + row_pos],
1030 (int)col_pos - 1, &eol, &display_len, 0);
1031 if (offset_in < p_editor_data->display_line_lengths[line_current - output_current_row + row_pos] &&
1032 p_editor_data->p_display_lines[line_current - output_current_row + row_pos][offset_in] < 0) // UTF8
1033 {
1034 col_pos = display_len + 3;
1035 }
1036 else
1037 {
1038 col_pos = display_len + 2;
1039 }
1040 if (col_pos <= p_editor_data->display_line_widths[line_current - output_current_row + row_pos])
1041 {
1042 break;
1043 }
1044 col_pos = 1; // continue to KEY_DOWN
1045 case KEY_DOWN:
1046 if (row_pos < MIN(screen_row_total, p_editor_data->display_line_total))
1047 {
1048 row_pos++;
1049 col_pos = MIN(col_pos, MAX(1, p_editor_data->display_line_widths[line_current - output_current_row + row_pos]));
1050 break;
1051 }
1052 if (line_current - output_current_row + row_pos == p_editor_data->display_line_total - 1) // row_pos at end line
1053 {
1054 // last display line does NOT have \n in the end
1055 col_pos = p_editor_data->display_line_widths[line_current - output_current_row + row_pos] + 1;
1056 break;
1057 }
1058 line_current += (screen_row_total - (output_current_row - screen_begin_row));
1059 output_current_row = screen_row_total;
1060 output_end_row = SCREEN_ROWS - 1;
1061 col_pos = MIN(col_pos, MAX(1, p_editor_data->display_line_widths[line_current - output_current_row + row_pos]));
1062 moveto(SCREEN_ROWS, 0);
1063 clrtoeol();
1064 // prints("\033[S"); // Scroll up 1 line
1065 prints("\n"); // Legacy Cterm only works with this line
1066 break;
1067 case KEY_PGUP:
1068 if (line_current - output_current_row < 0) // Reach begin
1069 {
1070 break;
1071 }
1072 line_current -= ((screen_row_total - 1) + (output_current_row - screen_begin_row));
1073 if (line_current < 0)
1074 {
1075 line_current = 0;
1076 }
1077 output_current_row = screen_begin_row;
1078 output_end_row = SCREEN_ROWS - 1;
1079 col_pos = MIN(col_pos, MAX(1, p_editor_data->display_line_widths[line_current - output_current_row + row_pos]));
1080 clrline(output_current_row, SCREEN_ROWS);
1081 break;
1082 case KEY_PGDN:
1083 if (line_current + screen_row_total - (output_current_row - screen_begin_row) >= p_editor_data->display_line_total) // Reach end
1084 {
1085 break;
1086 }
1087 line_current += (screen_row_total - 1) - (output_current_row - screen_begin_row);
1088 if (line_current + screen_row_total > p_editor_data->display_line_total) // No enough lines to display
1089 {
1090 line_current = p_editor_data->display_line_total - screen_row_total;
1091 }
1092 output_current_row = screen_begin_row;
1093 output_end_row = SCREEN_ROWS - 1;
1094 col_pos = MIN(col_pos, MAX(1, p_editor_data->display_line_widths[line_current - output_current_row + row_pos]));
1095 clrline(output_current_row, SCREEN_ROWS);
1096 break;
1097 case KEY_F1:
1098 if (!show_help) // Not reentrant
1099 {
1100 break;
1101 }
1102 // Display help information
1103 show_help = 0;
1104 display_file(DATA_READ_HELP, 1);
1105 show_help = 1;
1106 case KEY_F5:
1107 // Refresh after display help information
1108 line_current -= (output_current_row - screen_begin_row);
1109 output_current_row = screen_begin_row;
1110 output_end_row = SCREEN_ROWS - 1;
1111 clrline(output_current_row, SCREEN_ROWS);
1112 break;
1113 case 0: // Refresh bottom line
1114 break;
1115 default:
1116 input_ok = 0;
1117 break;
1118 }
1119
1120 BBS_last_access_tm = time(NULL);
1121
1122 if (input_ok)
1123 {
1124 break;
1125 }
1126
1127 ch = igetch_t(MAX_DELAY_TIME);
1128 }
1129
1130 continue;
1131 }
1132
1133 len = p_editor_data->display_line_lengths[line_current];
1134 if (len >= sizeof(buffer))
1135 {
1136 log_error("Buffer overflow: len=%ld line=%ld \n", len, line_current);
1137 len = sizeof(buffer) - 1;
1138 }
1139 else if (len < 0)
1140 {
1141 log_error("Incorrect line offsets: len=%ld line=%ld \n", len, line_current);
1142 len = 0;
1143 }
1144
1145 // memcpy(buffer, p_editor_data->p_display_lines[line_current], (size_t)len);
1146 // Replace '\033' with '*'
1147 p_str = p_editor_data->p_display_lines[line_current];
1148 for (i = 0, j = 0; i < len; i++)
1149 {
1150 if (p_str[i] == '\033')
1151 {
1152 memcpy(buffer + j, EDITOR_ESC_DISPLAY_STR, sizeof(EDITOR_ESC_DISPLAY_STR) - 1);
1153 j += (int)(sizeof(EDITOR_ESC_DISPLAY_STR) - 1);
1154 }
1155 else
1156 {
1157 buffer[j] = p_str[i];
1158 j++;
1159 }
1160 }
1161 buffer[j] = '\0';
1162
1163 moveto(output_current_row, 0);
1164 clrtoeol();
1165 prints("%s", buffer);
1166 line_current++;
1167 output_current_row++;
1168 }
1169
1170 cleanup:
1171 return ch;
1172 }

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