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

Contents of /lbbs/src/screen.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.127 - (show annotations)
Sat Nov 8 08:21:31 2025 UTC (4 months, 1 week ago) by sysadm
Branch: MAIN
Changes since 1.126: +19 -5 lines
Content type: text/x-csrc
Support dynamic wide-character display width in str_process and editor related functions
Add dynamic / fixed wide-character display width selection

1 /* SPDX-License-Identifier: GPL-3.0-or-later */
2 /*
3 * screen
4 * - advanced telnet-based user interactive input / output features
5 *
6 * Copyright (C) 2004-2025 Leaflet <leaflet@leafok.com>
7 */
8
9 #include "bbs.h"
10 #include "common.h"
11 #include "editor.h"
12 #include "file_loader.h"
13 #include "io.h"
14 #include "log.h"
15 #include "login.h"
16 #include "screen.h"
17 #include "str_process.h"
18 #include <ctype.h>
19 #include <errno.h>
20 #include <fcntl.h>
21 #include <string.h>
22 #include <stdlib.h>
23 #include <unistd.h>
24 #include <wchar.h>
25 #include <sys/param.h>
26 #include <sys/stat.h>
27 #include <sys/shm.h>
28 #include <sys/types.h>
29
30 const char CTRL_SEQ_CLR_LINE[] = "\033[K";
31
32 static const int ACTIVE_BOARD_HEIGHT = 8;
33
34 static const int STR_TOP_LEFT_MAX_LEN = 80;
35 static const int STR_TOP_MIDDLE_MAX_LEN = 40;
36 static const int STR_TOP_RIGHT_MAX_LEN = 80;
37
38 static size_t get_time_str(char *s, size_t len)
39 {
40 time_t curtime;
41 struct tm local_tm;
42
43 time(&curtime);
44 localtime_r(&curtime, &local_tm);
45 size_t j = strftime(s, len, "%m/%d %H:%M %Z", &local_tm);
46
47 return j;
48 }
49
50 void moveto(int row, int col)
51 {
52 if (row >= 0)
53 {
54 prints("\033[%d;%dH", row, col);
55 }
56 else
57 {
58 prints("\r");
59 }
60 }
61
62 void clrtoeol()
63 {
64 prints(CTRL_SEQ_CLR_LINE);
65 }
66
67 void clrline(int line_begin, int line_end)
68 {
69 int i;
70
71 for (i = line_begin; i <= line_end; i++)
72 {
73 moveto(i, 0);
74 prints(CTRL_SEQ_CLR_LINE);
75 }
76 }
77
78 void clrtobot(int line_begin)
79 {
80 moveto(line_begin, 0);
81 prints("\033[J");
82 moveto(line_begin, 0);
83 }
84
85 void clearscr()
86 {
87 prints("\033[2J");
88 moveto(0, 0);
89 }
90
91 inline int press_any_key()
92 {
93 return press_any_key_ex(" \033[1;33m按任意键继续...\033[m", 60);
94 }
95
96 int press_any_key_ex(const char *msg, int sec)
97 {
98 int ch = 0;
99 int duration = 0;
100 time_t t_begin = time(NULL);
101
102 moveto(SCREEN_ROWS, 0);
103 clrtoeol();
104
105 prints(msg);
106 iflush();
107
108 igetch_reset();
109
110 do
111 {
112 ch = igetch_t(sec - duration);
113 duration = (int)(time(NULL) - t_begin);
114 } while (!SYS_server_exit && ch == 0 && duration < 60);
115
116 return ch;
117 }
118
119 void set_input_echo(int echo)
120 {
121 if (echo)
122 {
123 outc('\x83'); // ASCII code 131
124 }
125 else
126 {
127 // outc ('\x85'); // ASCII code 133
128 prints("\xff\xfb\x01\xff\xfb\x03");
129 }
130 iflush();
131 }
132
133 static int _str_input(char *buffer, int buf_size, int max_display_len, enum io_echo_t echo_mode)
134 {
135 int ch;
136 int offset = 0;
137 int eol;
138 int display_len;
139 char input_str[5];
140 int str_len = 0;
141 wchar_t wcs[2];
142 char c;
143
144 buffer[buf_size - 1] = '\0';
145 offset = split_line(buffer, max_display_len, &eol, &display_len, 0);
146
147 igetch_reset();
148
149 while (!SYS_server_exit)
150 {
151 ch = igetch_t(MIN(BBS_max_user_idle_time, 60));
152
153 if (ch == CR)
154 {
155 break;
156 }
157 else if (ch == KEY_TIMEOUT || ch == KEY_NULL) // timeout or broken pipe
158 {
159 return -1;
160 }
161 else if (ch == LF || ch == '\0')
162 {
163 continue;
164 }
165 else if (ch == BACKSPACE)
166 {
167 if (offset > 0)
168 {
169 offset--;
170 if (buffer[offset] < 0 || buffer[offset] > 127) // UTF8
171 {
172 while (offset > 0 && (buffer[offset] & 0xc0) != 0xc0)
173 {
174 offset--;
175 }
176 display_len--;
177 prints("\033[D \033[D");
178 }
179 buffer[offset] = '\0';
180 display_len--;
181 prints("\033[D \033[D");
182 iflush();
183 }
184 continue;
185 }
186 else if (ch > 255 || iscntrl(ch))
187 {
188 continue;
189 }
190 else if ((ch & 0xff80) == 0x80) // head of multi-byte character
191 {
192 str_len = 0;
193 c = (char)(ch & 0xf0);
194 while (c & 0x80)
195 {
196 input_str[str_len] = (char)(ch - 256);
197 str_len++;
198 c = (c & 0x7f) << 1;
199
200 if ((c & 0x80) == 0) // Input completed
201 {
202 break;
203 }
204
205 // Expect additional bytes of input
206 ch = igetch(100); // 0.1 second
207 if (ch == KEY_NULL || ch == KEY_TIMEOUT) // Ignore received bytes if no futher input
208 {
209 #ifdef _DEBUG
210 log_error("Ignore %d bytes of incomplete UTF8 character\n", str_len);
211 #endif
212 str_len = 0;
213 break;
214 }
215 }
216 input_str[str_len] = '\0';
217
218 if (str_len == 0) // Incomplete input
219 {
220 continue;
221 }
222
223 if (mbstowcs(wcs, input_str, 1) == (size_t)-1)
224 {
225 log_error("mbstowcs() error\n");
226 }
227 if (offset + str_len > buf_size - 1 || display_len + (UTF8_fixed_width ? 2 : wcwidth(wcs[0])) > max_display_len) // No enough space for Chinese character
228 {
229 outc('\a');
230 iflush();
231 continue;
232 }
233
234 memcpy(buffer + offset, input_str, (size_t)str_len);
235 offset += str_len;
236 buffer[offset] = '\0';
237 display_len += 2;
238
239 switch (echo_mode)
240 {
241 case DOECHO:
242 prints(input_str);
243 break;
244 case NOECHO:
245 prints("**");
246 break;
247 }
248 }
249 else if (ch >= 32 && ch < 127) // Printable character
250 {
251 if (offset + 1 > buf_size - 1 || display_len + 1 > max_display_len)
252 {
253 outc('\a');
254 iflush();
255 continue;
256 }
257
258 buffer[offset++] = (char)ch;
259 buffer[offset] = '\0';
260 display_len++;
261
262 switch (echo_mode)
263 {
264 case DOECHO:
265 outc((char)ch);
266 break;
267 case NOECHO:
268 outc('*');
269 break;
270 }
271 }
272 else // Invalid character
273 {
274 continue;
275 }
276
277 iflush();
278 }
279
280 return offset;
281 }
282
283 int str_input(char *buffer, int buf_size, enum io_echo_t echo_mode)
284 {
285 int len;
286
287 buffer[0] = '\0';
288
289 len = _str_input(buffer, buf_size, buf_size, echo_mode);
290
291 prints("\r\n");
292 iflush();
293
294 return len;
295 }
296
297 int get_data(int row, int col, char *prompt, char *buffer, int buf_size, int max_display_len)
298 {
299 int len = 0;
300 int col_cur = 0;
301 int ch;
302 int offset = 0;
303 int eol;
304 int display_len;
305 char input_str[5];
306 wchar_t wcs[2];
307 int str_len = 0;
308 char c;
309
310 buffer[buf_size - 1] = '\0';
311 offset = split_line(buffer, max_display_len, &eol, &display_len, 0);
312 buffer[offset] = '\0';
313 len = offset;
314 col_cur = col + str_length(prompt, 1) + display_len;
315
316 moveto(row, col);
317 prints("%s", prompt);
318 prints("%s", buffer);
319 prints("%*s", max_display_len - display_len, "");
320 moveto(row, col_cur);
321 iflush();
322
323 igetch_reset();
324
325 while (!SYS_server_exit)
326 {
327 ch = igetch_t(MIN(BBS_max_user_idle_time, 60));
328
329 if (ch == CR)
330 {
331 break;
332 }
333 else if (ch == KEY_TIMEOUT || ch == KEY_NULL) // timeout or broken pipe
334 {
335 return -1;
336 }
337 else if (ch == LF || ch == '\0')
338 {
339 continue;
340 }
341 else if (ch == BACKSPACE)
342 {
343 if (offset > 0)
344 {
345 str_len = 1;
346 offset--;
347 if (buffer[offset] < 0 || buffer[offset] > 127) // UTF8
348 {
349 while (offset > 0 && (buffer[offset] & 0xc0) != 0xc0)
350 {
351 str_len++;
352 offset--;
353 }
354 display_len--;
355 col_cur--;
356 }
357
358 memmove(buffer + offset, buffer + offset + str_len, (size_t)(len - offset - str_len));
359 len -= str_len;
360 buffer[len] = '\0';
361 display_len--;
362 col_cur--;
363
364 moveto(row, col_cur);
365 prints("%s", buffer + offset);
366 prints("%*s", max_display_len - display_len, "");
367 moveto(row, col_cur);
368 iflush();
369 }
370 continue;
371 }
372 else if (ch == KEY_DEL)
373 {
374 if (offset < len)
375 {
376 if ((buffer[offset] & 0x80) == 0x80) // head of multi-byte character
377 {
378 str_len = 0;
379 c = (char)(buffer[offset] & 0xf0);
380 while (c & 0x80)
381 {
382 str_len++;
383 c = (c & 0x7f) << 1;
384 }
385 display_len--;
386 }
387 else
388 {
389 str_len = 1;
390 }
391
392 memmove(buffer + offset, buffer + offset + str_len, (size_t)(len - offset - str_len));
393 len -= str_len;
394 buffer[len] = '\0';
395 display_len--;
396
397 moveto(row, col_cur);
398 prints("%s", buffer + offset);
399 prints("%*s", max_display_len - display_len, "");
400 moveto(row, col_cur);
401 iflush();
402 }
403 continue;
404 }
405 else if (ch == KEY_LEFT)
406 {
407 if (offset > 0)
408 {
409 str_len = 1;
410 offset--;
411 if (buffer[offset] < 0 || buffer[offset] > 127) // UTF8
412 {
413 while (offset > 0 && (buffer[offset] & 0xc0) != 0xc0)
414 {
415 str_len++;
416 offset--;
417 }
418 col_cur--;
419 }
420 col_cur--;
421
422 moveto(row, col_cur);
423 iflush();
424 }
425 continue;
426 }
427 else if (ch == KEY_RIGHT)
428 {
429 if (offset < len)
430 {
431 str_len = 0;
432 if ((buffer[offset] & 0x80) == 0x80) // head of multi-byte character
433 {
434 c = (char)(buffer[offset] & 0xf0);
435 while (c & 0x80)
436 {
437 str_len++;
438 c = (c & 0x7f) << 1;
439 }
440 col_cur++;
441 }
442 else
443 {
444 str_len = 1;
445 }
446
447 col_cur++;
448 offset += str_len;
449
450 moveto(row, col_cur);
451 iflush();
452 }
453 continue;
454 }
455 else if (ch == KEY_HOME || ch == KEY_UP)
456 {
457 if (offset > 0)
458 {
459 offset = 0;
460 col_cur = col + str_length(prompt, 1);
461
462 moveto(row, col_cur);
463 iflush();
464 }
465 continue;
466 }
467 else if (ch == KEY_END || ch == KEY_DOWN)
468 {
469 if (offset < len)
470 {
471 offset = len;
472 col_cur = col + str_length(prompt, 1) + display_len;
473
474 moveto(row, col_cur);
475 iflush();
476 }
477 continue;
478 }
479 else if (ch > 255 || iscntrl(ch))
480 {
481 continue;
482 }
483 else if ((ch & 0xff80) == 0x80) // head of multi-byte character
484 {
485 str_len = 0;
486 c = (char)(ch & 0xf0);
487 while (c & 0x80)
488 {
489 input_str[str_len] = (char)(ch - 256);
490 str_len++;
491 c = (c & 0x7f) << 1;
492
493 if ((c & 0x80) == 0) // Input completed
494 {
495 break;
496 }
497
498 // Expect additional bytes of input
499 ch = igetch(100); // 0.1 second
500 if (ch == KEY_NULL || ch == KEY_TIMEOUT) // Ignore received bytes if no futher input
501 {
502 #ifdef _DEBUG
503 log_error("Ignore %d bytes of incomplete UTF8 character\n", str_len);
504 #endif
505 str_len = 0;
506 break;
507 }
508 }
509 input_str[str_len] = '\0';
510
511 if (str_len == 0) // Incomplete input
512 {
513 continue;
514 }
515
516 if (mbstowcs(wcs, input_str, 1) == (size_t)-1)
517 {
518 log_error("mbstowcs() error\n");
519 }
520 if (len + str_len > buf_size - 1 || display_len + (UTF8_fixed_width ? 2 : wcwidth(wcs[0])) > max_display_len) // No enough space for Chinese character
521 {
522 outc('\a');
523 iflush();
524 continue;
525 }
526
527 memmove(buffer + offset + str_len, buffer + offset, (size_t)(len - offset));
528 memcpy(buffer + offset, input_str, (size_t)str_len);
529 len += str_len;
530 buffer[len] = '\0';
531 display_len += 2;
532
533 moveto(row, col_cur);
534 prints("%s", buffer + offset);
535 prints("%*s", max_display_len - display_len, "");
536
537 col_cur += 2;
538
539 moveto(row, col_cur);
540 iflush();
541
542 offset += str_len;
543 }
544 else if (ch >= 32 && ch < 127) // Printable character
545 {
546 if (len + 1 > buf_size - 1 || display_len + 1 > max_display_len)
547 {
548 outc('\a');
549 iflush();
550 continue;
551 }
552
553 memmove(buffer + offset + 1, buffer + offset, (size_t)(len - offset));
554 buffer[offset] = (char)ch;
555 len++;
556 buffer[len] = '\0';
557 display_len++;
558
559 moveto(row, col_cur);
560 prints("%s", buffer + offset);
561 prints("%*s", max_display_len - display_len, "");
562
563 col_cur++;
564
565 moveto(row, col_cur);
566 iflush();
567
568 offset++;
569 }
570 else // Invalid character
571 {
572 continue;
573 }
574 }
575
576 return len;
577 }
578
579 int display_data(const void *p_data, long display_line_total, const long *p_line_offsets, int eof_exit,
580 display_data_key_handler key_handler, const char *help_filename)
581 {
582 static int show_help = 1;
583 char buffer[LINE_BUFFER_LEN];
584 DISPLAY_CTX ctx;
585 int ch = 0;
586 int input_ok;
587 const int screen_begin_row = 1;
588 const int screen_row_total = SCREEN_ROWS - screen_begin_row;
589 int output_current_row = screen_begin_row;
590 int output_end_row = SCREEN_ROWS - 1;
591 long int line_current = 0;
592 long int len;
593 long int percentile;
594 int loop;
595 int eol, display_len;
596
597 clrline(output_current_row, SCREEN_ROWS);
598
599 // update msg_ext with extended key handler
600 if (key_handler(&ch, &ctx) != 0)
601 {
602 return ch;
603 }
604
605 loop = 1;
606 while (!SYS_server_exit && loop)
607 {
608 if (eof_exit > 0 && line_current >= display_line_total)
609 {
610 if (eof_exit == 1)
611 {
612 ch = press_any_key();
613 }
614 else // if (eof_exit == 2)
615 {
616 iflush();
617 }
618
619 loop = 0;
620 break;
621 }
622
623 if (line_current >= display_line_total || output_current_row > output_end_row)
624 {
625 ctx.reach_begin = (line_current < output_current_row ? 1 : 0);
626
627 if (line_current - (output_current_row - screen_begin_row) + screen_row_total < display_line_total)
628 {
629 percentile = (line_current - (output_current_row - screen_begin_row) + screen_row_total) * 100 / display_line_total;
630 ctx.reach_end = 0;
631 }
632 else
633 {
634 percentile = 100;
635 ctx.reach_end = 1;
636 }
637
638 ctx.line_top = line_current - (output_current_row - screen_begin_row) + 1;
639 ctx.line_bottom = MIN(line_current - (output_current_row - screen_begin_row) + screen_row_total, display_line_total);
640
641 snprintf(buffer, sizeof(buffer),
642 "\033[1;44;33m第\033[32m%ld\033[33m-\033[32m%ld\033[33m行 (\033[32m%ld%%\033[33m) %s",
643 ctx.line_top,
644 ctx.line_bottom,
645 percentile,
646 ctx.msg);
647
648 len = split_line(buffer, SCREEN_COLS, &eol, &display_len, 1);
649 for (; display_len < SCREEN_COLS; display_len++)
650 {
651 buffer[len++] = ' ';
652 }
653 buffer[len] = '\0';
654 strncat(buffer, "\033[m", sizeof(buffer) - 1 - strnlen(buffer, sizeof(buffer)));
655
656 moveto(SCREEN_ROWS, 0);
657 prints("%s", buffer);
658 iflush();
659
660 input_ok = 0;
661 while (!SYS_server_exit && !input_ok)
662 {
663 ch = igetch_t(BBS_max_user_idle_time);
664 input_ok = 1;
665
666 if (ch != KEY_NULL && ch != KEY_TIMEOUT)
667 {
668 BBS_last_access_tm = time(NULL);
669 }
670
671 // extended key handler
672 if (key_handler(&ch, &ctx) != 0)
673 {
674 goto cleanup;
675 }
676
677 switch (ch)
678 {
679 case KEY_NULL:
680 log_error("KEY_NULL\n");
681 goto cleanup;
682 case KEY_TIMEOUT:
683 log_error("User input timeout\n");
684 goto cleanup;
685 case KEY_HOME:
686 if (line_current - output_current_row < 0) // Reach begin
687 {
688 break;
689 }
690 line_current = 0;
691 output_current_row = screen_begin_row;
692 output_end_row = SCREEN_ROWS - 1;
693 clrline(output_current_row, SCREEN_ROWS);
694 break;
695 case KEY_END:
696 if (display_line_total < screen_row_total)
697 {
698 break;
699 }
700 line_current = display_line_total - screen_row_total;
701 output_current_row = screen_begin_row;
702 output_end_row = SCREEN_ROWS - 1;
703 clrline(output_current_row, SCREEN_ROWS);
704 break;
705 case KEY_UP:
706 if (line_current - output_current_row < 0) // Reach begin
707 {
708 break;
709 }
710 line_current -= output_current_row;
711 output_current_row = screen_begin_row;
712 // screen_end_line = screen_begin_line;
713 // prints("\033[T"); // Scroll down 1 line
714 output_end_row = SCREEN_ROWS - 1; // Legacy Fterm only works with this line
715 break;
716 case CR:
717 case KEY_DOWN:
718 if (line_current + (screen_row_total - (output_current_row - screen_begin_row)) >= display_line_total) // Reach end
719 {
720 break;
721 }
722 line_current += (screen_row_total - (output_current_row - screen_begin_row));
723 output_current_row = screen_row_total;
724 output_end_row = SCREEN_ROWS - 1;
725 moveto(SCREEN_ROWS, 0);
726 clrtoeol();
727 // prints("\033[S"); // Scroll up 1 line
728 prints("\n"); // Legacy Cterm only works with this line
729 break;
730 case KEY_PGUP:
731 if (line_current - output_current_row < 0) // Reach begin
732 {
733 break;
734 }
735 line_current -= ((screen_row_total - 1) + (output_current_row - screen_begin_row));
736 if (line_current < 0)
737 {
738 line_current = 0;
739 }
740 output_current_row = screen_begin_row;
741 output_end_row = SCREEN_ROWS - 1;
742 clrline(output_current_row, SCREEN_ROWS);
743 break;
744 case KEY_SPACE:
745 case KEY_PGDN:
746 if (line_current + screen_row_total - (output_current_row - screen_begin_row) >= display_line_total) // Reach end
747 {
748 break;
749 }
750 line_current += (screen_row_total - 1) - (output_current_row - screen_begin_row);
751 if (line_current + screen_row_total > display_line_total) // No enough lines to display
752 {
753 line_current = display_line_total - screen_row_total;
754 }
755 output_current_row = screen_begin_row;
756 output_end_row = SCREEN_ROWS - 1;
757 clrline(output_current_row, SCREEN_ROWS);
758 break;
759 case KEY_ESC:
760 case KEY_LEFT:
761 loop = 0;
762 break;
763 case 'h':
764 if (!show_help) // Not reentrant
765 {
766 break;
767 }
768 // Display help information
769 show_help = 0;
770 display_file(help_filename, 1);
771 show_help = 1;
772 case KEY_F5:
773 // Refresh after display help information
774 line_current -= (output_current_row - screen_begin_row);
775 output_current_row = screen_begin_row;
776 output_end_row = SCREEN_ROWS - 1;
777 clrline(output_current_row, SCREEN_ROWS);
778 break;
779 case 0: // Refresh bottom line
780 break;
781 default:
782 input_ok = 0;
783 break;
784 }
785 }
786
787 continue;
788 }
789
790 len = p_line_offsets[line_current + 1] - p_line_offsets[line_current];
791 if (len >= sizeof(buffer))
792 {
793 log_error("Buffer overflow: len=%ld(%ld - %ld) line=%ld \n",
794 len, p_line_offsets[line_current + 1], p_line_offsets[line_current], line_current);
795 len = sizeof(buffer) - 1;
796 }
797 else if (len < 0)
798 {
799 log_error("Incorrect line offsets: len=%ld(%ld - %ld) line=%ld \n",
800 len, p_line_offsets[line_current + 1], p_line_offsets[line_current], line_current);
801 len = 0;
802 }
803
804 memcpy(buffer, (const char *)p_data + p_line_offsets[line_current], (size_t)len);
805 buffer[len] = '\0';
806
807 moveto(output_current_row, 0);
808 clrtoeol();
809 prints("%s", buffer);
810 line_current++;
811 output_current_row++;
812 }
813
814 cleanup:
815 return ch;
816 }
817
818 int display_file_key_handler(int *p_key, DISPLAY_CTX *p_ctx)
819 {
820 switch (*p_key)
821 {
822 case 0: // Set msg
823 snprintf(p_ctx->msg, sizeof(p_ctx->msg),
824 "| 返回[\033[32m←\033[33m,\033[32mESC\033[33m] | "
825 "移动[\033[32m↑\033[33m/\033[32m↓\033[33m/\033[32mPgUp\033[33m/\033[32mPgDn\033[33m] | "
826 "帮助[\033[32mh\033[33m] |");
827 break;
828 }
829
830 return 0;
831 }
832
833 int display_file(const char *filename, int eof_exit)
834 {
835 int ret;
836 const void *p_shm;
837 size_t data_len;
838 long line_total;
839 const void *p_data;
840 const long *p_line_offsets;
841
842 if ((p_shm = get_file_shm_readonly(filename, &data_len, &line_total, &p_data, &p_line_offsets)) == NULL)
843 {
844 log_error("get_file_shm(%s) error\n", filename);
845 return KEY_NULL;
846 }
847
848 if (user_online_update("VIEW_FILE") < 0)
849 {
850 log_error("user_online_update(VIEW_FILE) error\n");
851 }
852
853 ret = display_data(p_data, line_total, p_line_offsets, eof_exit, display_file_key_handler, DATA_READ_HELP);
854
855 if (detach_file_shm(p_shm) < 0)
856 {
857 log_error("detach_file_shm(%s) error\n", filename);
858 }
859
860 return ret;
861 }
862
863 int show_top(const char *str_left, const char *str_middle, const char *str_right)
864 {
865 char str_left_f[STR_TOP_LEFT_MAX_LEN + 1];
866 char str_middle_f[STR_TOP_MIDDLE_MAX_LEN + 1];
867 char str_right_f[STR_TOP_RIGHT_MAX_LEN + 1];
868 int str_left_len;
869 int str_middle_len;
870 int str_right_len;
871 int eol;
872 int len;
873
874 strncpy(str_left_f, str_left, sizeof(str_left_f) - 1);
875 str_left_f[sizeof(str_left_f) - 1] = '\0';
876 len = split_line(str_left_f, STR_TOP_LEFT_MAX_LEN / 2, &eol, &str_left_len, 1);
877 str_left_f[len] = '\0';
878
879 strncpy(str_middle_f, str_middle, sizeof(str_middle_f) - 1);
880 str_middle_f[sizeof(str_middle_f) - 1] = '\0';
881 len = split_line(str_middle, STR_TOP_MIDDLE_MAX_LEN / 2, &eol, &str_middle_len, 1);
882 str_middle_f[len] = '\0';
883
884 strncpy(str_right_f, str_right, sizeof(str_right_f) - 1);
885 str_right_f[sizeof(str_right_f) - 1] = '\0';
886 len = split_line(str_right, STR_TOP_RIGHT_MAX_LEN / 2, &eol, &str_right_len, 1);
887 str_right_f[len] = '\0';
888
889 moveto(1, 0);
890 clrtoeol();
891 prints("\033[1;44;33m%s\033[37m%*s%s\033[33m%*s%s\033[m",
892 str_left_f, 44 - str_left_len - str_middle_len, "",
893 str_middle_f, 36 - str_right_len, "", str_right_f);
894
895 return 0;
896 }
897
898 int show_bottom(const char *msg)
899 {
900 char str_time[LINE_BUFFER_LEN];
901 int len_str_time;
902 time_t time_online;
903 struct tm *tm_online;
904 char msg_f[LINE_BUFFER_LEN];
905 int eol;
906 int len_msg;
907 int len;
908 int len_username;
909 char str_tm_online[LINE_BUFFER_LEN];
910 int len_str_tm_online;
911
912 len_str_time = (int)get_time_str(str_time, sizeof(str_time));
913
914 msg_f[0] = '\0';
915 len_msg = 0;
916 if (msg != NULL)
917 {
918 strncpy(msg_f, msg, sizeof(msg_f) - 1);
919 msg_f[sizeof(msg_f) - 1] = '\0';
920 len = split_line(msg_f, 23, &eol, &len_msg, 1);
921 msg_f[len] = '\0';
922 }
923
924 len_username = (int)strnlen(BBS_username, sizeof(BBS_username));
925
926 time_online = time(NULL) - BBS_login_tm;
927 tm_online = gmtime(&time_online);
928 if (tm_online->tm_mday > 1)
929 {
930 snprintf(str_tm_online, sizeof(str_tm_online),
931 "\033[36m%d\033[33md \033[36m%d\033[33m:\033[36m%.2d",
932 tm_online->tm_mday - 1, tm_online->tm_hour, tm_online->tm_min);
933 }
934 else
935 {
936 snprintf(str_tm_online, sizeof(str_tm_online),
937 "\033[36m%d\033[33m:\033[36m%.2d",
938 tm_online->tm_hour, tm_online->tm_min);
939 }
940 len_str_tm_online = str_length(str_tm_online, 1);
941
942 moveto(SCREEN_ROWS, 0);
943 clrtoeol();
944 prints("\033[1;44;33m时间[\033[36m%s\033[33m]%s%*s \033[33m用户[\033[36m%s\033[33m][%s\033[33m]\033[m",
945 str_time, msg_f, 65 - len_str_time - len_msg - len_username - len_str_tm_online,
946 "", BBS_username, str_tm_online);
947
948 return 0;
949 }
950
951 int show_active_board()
952 {
953 static int line_current = 0;
954 static const void *p_shm = NULL;
955 static size_t data_len;
956 static long line_total;
957 static const void *p_data;
958 static const long *p_line_offsets;
959
960 static time_t t_last_show = 0;
961 static int line_last = 0;
962
963 char buffer[LINE_BUFFER_LEN];
964 long int len;
965
966 if (p_shm == NULL)
967 {
968 if ((p_shm = get_file_shm_readonly(DATA_ACTIVE_BOARD, &data_len, &line_total, &p_data, &p_line_offsets)) == NULL)
969 {
970 log_error("get_file_shm(%s) error\n", DATA_ACTIVE_BOARD);
971 return KEY_NULL;
972 }
973 }
974
975 if (time(NULL) - t_last_show >= 10)
976 {
977 line_last = line_current;
978 t_last_show = time(NULL);
979 }
980 else
981 {
982 line_current = line_last;
983 }
984
985 clrline(2, 2 + ACTIVE_BOARD_HEIGHT);
986
987 for (int i = 0; i < ACTIVE_BOARD_HEIGHT; i++)
988 {
989 len = p_line_offsets[line_current + 1] - p_line_offsets[line_current];
990 if (len >= LINE_BUFFER_LEN)
991 {
992 log_error("buffer overflow: len=%ld(%ld - %ld) line=%ld \n",
993 len, p_line_offsets[line_current + 1], p_line_offsets[line_current], line_current);
994 len = LINE_BUFFER_LEN - 1;
995 }
996
997 memcpy(buffer, (const char *)p_data + p_line_offsets[line_current], (size_t)len);
998 buffer[len] = '\0';
999
1000 moveto(3 + i, 0);
1001 prints("%s", buffer);
1002
1003 line_current++;
1004 if (line_current >= line_total)
1005 {
1006 line_current = 0;
1007 break;
1008 }
1009 }
1010
1011 return 0;
1012 }

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