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

Contents of /lbbs/src/section_list_display.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.65 - (show annotations)
Mon Nov 3 06:45:18 2025 UTC (4 months, 1 week ago) by sysadm
Branch: MAIN
Changes since 1.64: +1 -0 lines
Content type: text/x-csrc
Add required header file

1 /***************************************************************************
2 section_list_display.c - 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 "article_cache.h"
18 #include "article_favor.h"
19 #include "article_op.h"
20 #include "article_post.h"
21 #include "article_view_log.h"
22 #include "article_del.h"
23 #include "common.h"
24 #include "io.h"
25 #include "log.h"
26 #include "login.h"
27 #include "menu.h"
28 #include "menu_proc.h"
29 #include "section_list_display.h"
30 #include "section_list_loader.h"
31 #include "screen.h"
32 #include "str_process.h"
33 #include "user_info_display.h"
34 #include "user_list_display.h"
35 #include "user_priv.h"
36 #include <ctype.h>
37 #include <errno.h>
38 #include <string.h>
39 #include <time.h>
40 #include <sys/param.h>
41
42 static int32_t section_aid_locations[BBS_max_section] = {0};
43 static int section_topic_view_mode = 0;
44 static int section_topic_view_tid = -1;
45
46 enum select_cmd_t
47 {
48 EXIT_SECTION = 0,
49 VIEW_ARTICLE,
50 CHANGE_PAGE,
51 SHOW_HELP,
52 CHANGE_NAME_DISPLAY,
53 POST_ARTICLE,
54 EDIT_ARTICLE,
55 DELETE_ARTICLE,
56 QUERY_ARTICLE,
57 QUERY_USER,
58 SET_FAVOR_ARTICLE,
59 UNSET_FAVOR_ARTICLE,
60 FIRST_TOPIC_ARTICLE,
61 LAST_TOPIC_ARTICLE,
62 SCAN_NEW_ARTICLE,
63 SCAN_ARTICLE_BACKWARD_BY_USER,
64 SCAN_ARTICLE_FORWARD_BY_USER,
65 VIEW_EX_DIR,
66 SHOW_TOP10,
67 SEARCH_USER,
68 };
69
70 static int section_list_draw_items(int page_id, ARTICLE *p_articles[], int article_count, int display_nickname, int ontop_start_offset)
71 {
72 char str_time[LINE_BUFFER_LEN];
73 struct tm tm_sub;
74 char title_f[BBS_article_title_max_len + 1];
75 int title_f_len;
76 int eol;
77 int len;
78 int i;
79 size_t j;
80 char article_flag;
81 int is_viewed;
82 int is_favor;
83 time_t tm_now;
84
85 time(&tm_now);
86
87 clrline(4, 23);
88
89 for (i = 0; i < article_count; i++)
90 {
91 if (p_articles[i]->uid == BBS_priv.uid)
92 {
93 is_viewed = 1;
94 }
95 else
96 {
97 is_viewed = article_view_log_is_viewed(p_articles[i]->aid, &BBS_article_view_log);
98 if (is_viewed < 0)
99 {
100 log_error("article_view_log_is_viewed(aid=%d) error\n", p_articles[i]->aid);
101 is_viewed = 0;
102 }
103 }
104
105 if (p_articles[i]->tid == 0)
106 {
107 is_favor = article_favor_check(p_articles[i]->aid, &BBS_article_favor);
108 if (is_favor < 0)
109 {
110 log_error("article_favor_check(aid=%d) error\n", p_articles[i]->aid);
111 is_favor = 0;
112 }
113 }
114 else
115 {
116 is_favor = 0;
117 }
118
119 if (p_articles[i]->excerption)
120 {
121 article_flag = (is_viewed ? 'm' : 'M');
122 }
123 else if (p_articles[i]->lock && is_viewed)
124 {
125 article_flag = 'x';
126 }
127 else
128 {
129 article_flag = (is_viewed ? ' ' : 'N');
130 }
131
132 localtime_r(&p_articles[i]->sub_dt, &tm_sub);
133 if (tm_now - p_articles[i]->sub_dt < 3600 * 24 * 365)
134 {
135 strftime(str_time, sizeof(str_time), "%b %e ", &tm_sub);
136 }
137 else
138 {
139 strftime(str_time, sizeof(str_time), "%m/%Y", &tm_sub);
140 }
141
142 strncpy(title_f, (p_articles[i]->tid == 0 ? "● " : ""), sizeof(title_f) - 1);
143 title_f[sizeof(title_f) - 1] = '\0';
144 strncat(title_f, (p_articles[i]->transship ? "[转载]" : ""), sizeof(title_f) - 1 - strnlen(title_f, sizeof(title_f)));
145
146 // Rewrite title with "Re: Re: " prefix into "Re: ... "
147 j = 0;
148 if (p_articles[i]->tid != 0)
149 {
150 while (strncmp(p_articles[i]->title + j, "Re: ", strlen("Re: ")) == 0)
151 {
152 j += strlen("Re: ");
153 }
154 if (j >= strlen("Re: Re: "))
155 {
156 strncat(title_f, "Re: ... ", sizeof(title_f) - 1 - strnlen(title_f, sizeof(title_f)));
157 }
158 else
159 {
160 j = 0;
161 }
162 }
163 strncat(title_f, p_articles[i]->title + j, sizeof(title_f) - 1 - strnlen(title_f, sizeof(title_f)));
164
165 len = split_line(title_f, 59 - (display_nickname ? BBS_nickname_max_len / 2 : BBS_username_max_len), &eol, &title_f_len, 1);
166 if (title_f[len] != '\0')
167 {
168 title_f[len] = '\0';
169 }
170
171 moveto(4 + i, 1);
172 if (i >= ontop_start_offset)
173 {
174 prints(" \033[1;33m[提示]\033[m%c%c %s%*s %s %s%s\033[m",
175 (is_favor ? '@' : ' '),
176 article_flag,
177 (display_nickname ? p_articles[i]->nickname : p_articles[i]->username),
178 (display_nickname ? BBS_nickname_max_len / 2 - str_length(p_articles[i]->nickname, 1)
179 : BBS_username_max_len - str_length(p_articles[i]->username, 1)),
180 "",
181 str_time,
182 (p_articles[i]->aid == section_topic_view_tid
183 ? "\033[1;33m"
184 : (p_articles[i]->tid == section_topic_view_tid
185 ? "\033[1;36m"
186 : "")),
187 title_f);
188 }
189 else
190 {
191 prints(" %s%7d\033[m%c%c %s%*s %s %s%s\033[m",
192 (p_articles[i]->aid == section_topic_view_tid
193 ? "\033[1;33m"
194 : (p_articles[i]->tid == section_topic_view_tid
195 ? "\033[1;36m"
196 : "")),
197 p_articles[i]->aid,
198 (is_favor ? '@' : ' '),
199 article_flag,
200 (display_nickname ? p_articles[i]->nickname : p_articles[i]->username),
201 (display_nickname ? BBS_nickname_max_len / 2 - str_length(p_articles[i]->nickname, 1)
202 : BBS_username_max_len - str_length(p_articles[i]->username, 1)),
203 "",
204 str_time,
205 (p_articles[i]->aid == section_topic_view_tid
206 ? "\033[1;33m"
207 : (p_articles[i]->tid == section_topic_view_tid
208 ? "\033[1;36m"
209 : "")),
210 title_f);
211 }
212 }
213
214 return 0;
215 }
216
217 static int section_list_draw_screen(const char *sname, const char *stitle, const char *master_list, int display_nickname)
218 {
219 char str_section_master[LINE_BUFFER_LEN] = "诚征版主中";
220 char str_section_name[LINE_BUFFER_LEN];
221
222 if (master_list[0] != '\0')
223 {
224 snprintf(str_section_master, sizeof(str_section_master), "版主:%s", master_list);
225 }
226 snprintf(str_section_name, sizeof(str_section_name), "讨论区 [%s]", sname);
227
228 clearscr();
229 show_top(str_section_master, stitle, str_section_name);
230 moveto(2, 0);
231 prints("返回[\033[1;32m←\033[0;37m,\033[1;32mESC\033[0;37m] 选择[\033[1;32m↑\033[0;37m,\033[1;32m↓\033[0;37m] "
232 "阅读[\033[1;32m→\033[0;37m,\033[1;32mENTER\033[0;37m] 发表[\033[1;32mCtrl-P\033[0;37m] "
233 "%s[\033[1;32mn\033[0;37m] 精华区[\033[1;32mx\033[0;37m] 帮助[\033[1;32mh\033[0;37m]\033[m",
234 (display_nickname ? "用户名" : "昵称"));
235 moveto(3, 0);
236 if (display_nickname)
237 {
238 prints("\033[44;37m \033[1;37m 编 号 发布者昵称 日 期 文章标题 \033[m");
239 }
240 else
241 {
242 prints("\033[44;37m \033[1;37m 编 号 发 布 者 日 期 文章标题 \033[m");
243 }
244
245 return 0;
246 }
247
248 static enum select_cmd_t section_list_select(int total_page, int item_count, int *p_page_id, int *p_selected_index)
249 {
250 int old_page_id = *p_page_id;
251 int old_selected_index = *p_selected_index;
252 int ch;
253 time_t last_refresh_tm = time(NULL);
254
255 if (item_count > 0 && *p_selected_index >= 0)
256 {
257 moveto(4 + *p_selected_index, 1);
258 outc('>');
259 iflush();
260 }
261
262 while (!SYS_server_exit)
263 {
264 ch = igetch(100);
265
266 if (ch != KEY_NULL && ch != KEY_TIMEOUT)
267 {
268 BBS_last_access_tm = time(NULL);
269 }
270
271 switch (ch)
272 {
273 case KEY_NULL: // broken pipe
274 log_error("KEY_NULL\n");
275 return EXIT_SECTION;
276 case KEY_TIMEOUT:
277 if (time(NULL) - BBS_last_access_tm >= MAX_DELAY_TIME)
278 {
279 log_error("User input timeout\n");
280 return EXIT_SECTION;
281 }
282 continue;
283 case KEY_ESC:
284 case KEY_LEFT:
285 return EXIT_SECTION;
286 case 'n':
287 return CHANGE_NAME_DISPLAY;
288 case CR:
289 case 'r':
290 case KEY_RIGHT:
291 if (item_count > 0)
292 {
293 return VIEW_ARTICLE;
294 }
295 break;
296 case Ctrl('P'):
297 return POST_ARTICLE;
298 case 'E':
299 if (item_count > 0)
300 {
301 return EDIT_ARTICLE;
302 }
303 break;
304 case 'd':
305 if (item_count > 0)
306 {
307 return DELETE_ARTICLE;
308 }
309 break;
310 case Ctrl('Q'):
311 if (item_count > 0)
312 {
313 return QUERY_ARTICLE;
314 }
315 break;
316 case Ctrl('A'):
317 if (item_count > 0)
318 {
319 return QUERY_USER;
320 }
321 break;
322 case 'F':
323 if (item_count > 0)
324 {
325 return SET_FAVOR_ARTICLE;
326 }
327 break;
328 case '-':
329 if (item_count > 0)
330 {
331 return UNSET_FAVOR_ARTICLE;
332 }
333 break;
334 case KEY_HOME:
335 *p_page_id = 0;
336 case 'P':
337 case KEY_PGUP:
338 *p_selected_index = 0;
339 case 'k':
340 case KEY_UP:
341 if (*p_selected_index <= 0)
342 {
343 if (*p_page_id > 0)
344 {
345 (*p_page_id)--;
346 *p_selected_index = BBS_article_limit_per_page - 1;
347 }
348 else if (ch == KEY_UP || ch == 'k') // Rotate to the tail of section list
349 {
350 if (total_page > 0)
351 {
352 *p_page_id = total_page - 1;
353 }
354 if (item_count > 0)
355 {
356 *p_selected_index = item_count - 1;
357 }
358 }
359 }
360 else
361 {
362 (*p_selected_index)--;
363 }
364 break;
365 case '$':
366 case KEY_END:
367 if (total_page > 0)
368 {
369 *p_page_id = total_page - 1;
370 }
371 case 'N':
372 case KEY_PGDN:
373 if (item_count > 0)
374 {
375 *p_selected_index = item_count - 1;
376 }
377 case 'j':
378 case KEY_DOWN:
379 if (*p_selected_index + 1 >= item_count) // next page
380 {
381 if (*p_page_id + 1 < total_page)
382 {
383 (*p_page_id)++;
384 *p_selected_index = 0;
385 }
386 else if (ch == KEY_DOWN || ch == 'j') // Rotate to the head of section list
387 {
388 *p_page_id = 0;
389 *p_selected_index = 0;
390 }
391 }
392 else
393 {
394 (*p_selected_index)++;
395 }
396 break;
397 case '=':
398 if (item_count > 0)
399 {
400 return FIRST_TOPIC_ARTICLE;
401 }
402 break;
403 case '\\':
404 if (item_count > 0)
405 {
406 return LAST_TOPIC_ARTICLE;
407 }
408 break;
409 case 'S':
410 if (item_count > 0)
411 {
412 return SCAN_NEW_ARTICLE;
413 }
414 break;
415 case 'A':
416 if (item_count > 0)
417 {
418 return SCAN_ARTICLE_BACKWARD_BY_USER;
419 }
420 break;
421 case 'a':
422 if (item_count > 0)
423 {
424 return SCAN_ARTICLE_FORWARD_BY_USER;
425 }
426 break;
427 case 'u':
428 return SEARCH_USER;
429 case 'h':
430 return SHOW_HELP;
431 case 'x':
432 return VIEW_EX_DIR;
433 case 'H':
434 return SHOW_TOP10;
435 default:
436 break;
437 }
438
439 if (old_page_id != *p_page_id)
440 {
441 return CHANGE_PAGE;
442 }
443
444 if (item_count > 0 && old_selected_index != *p_selected_index)
445 {
446 if (old_selected_index >= 0)
447 {
448 moveto(4 + old_selected_index, 1);
449 outc(' ');
450 }
451 if (*p_selected_index >= 0)
452 {
453 moveto(4 + *p_selected_index, 1);
454 outc('>');
455 }
456 iflush();
457
458 old_selected_index = *p_selected_index;
459 }
460
461 if (BBS_last_access_tm - last_refresh_tm >= BBS_section_list_load_interval)
462 {
463 return CHANGE_PAGE; // force section list refresh
464 }
465 }
466
467 return EXIT_SECTION;
468 }
469
470 static int display_article_key_handler(int *p_key, DISPLAY_CTX *p_ctx)
471 {
472 switch (*p_key)
473 {
474 case 'p':
475 case Ctrl('X'):
476 section_topic_view_mode = !section_topic_view_mode;
477 case 0: // Set msg
478 if (section_topic_view_mode)
479 {
480 snprintf(p_ctx->msg, sizeof(p_ctx->msg),
481 "| 返回[\033[32m←\033[33m,\033[32mESC\033[33m] "
482 "同主题阅读[\033[32m↑\033[33m/\033[32m↓\033[33m] "
483 "切换[\033[32mp\033[33m] 回复[\033[32mr\033[33m] 帮助[\033[32mh\033[33m] |");
484 }
485 else
486 {
487 snprintf(p_ctx->msg, sizeof(p_ctx->msg),
488 "| 返回[\033[32m←\033[33m,\033[32mESC\033[33m] "
489 "移动[\033[32m↑\033[33m/\033[32m↓\033[33m/\033[32mPgUp\033[33m/\033[32mPgDn\033[33m] "
490 "切换[\033[32mp\033[33m] 回复[\033[32mr\033[33m] 帮助[\033[32mh\033[33m] |");
491 }
492 *p_key = 0;
493 break;
494 case 'r': // Reply article
495 return 1;
496 case '=': // First topic article
497 return 1;
498 case '\\': // Last topic article
499 return 1;
500 case KEY_UP:
501 case KEY_PGUP:
502 case KEY_HOME:
503 if (p_ctx->reach_begin)
504 {
505 if (section_topic_view_mode)
506 {
507 *p_key = KEY_PGUP;
508 }
509 else
510 {
511 *p_key = KEY_UP;
512 }
513 return 1;
514 }
515 break;
516 case 'k':
517 if (section_topic_view_mode)
518 {
519 *p_key = KEY_PGUP;
520 }
521 else
522 {
523 *p_key = KEY_UP;
524 }
525 return 1;
526 case KEY_DOWN:
527 case KEY_PGDN:
528 case KEY_END:
529 if (p_ctx->reach_end)
530 {
531 if (section_topic_view_mode)
532 {
533 *p_key = KEY_PGDN;
534 }
535 else
536 {
537 *p_key = KEY_DOWN;
538 }
539 return 1;
540 }
541 break;
542 case 'j':
543 if (section_topic_view_mode)
544 {
545 *p_key = KEY_PGDN;
546 }
547 else
548 {
549 *p_key = KEY_DOWN;
550 }
551 return 1;
552 }
553
554 return 0;
555 }
556
557 int section_list_display(const char *sname, int32_t aid)
558 {
559 static int display_nickname = 0;
560
561 SECTION_LIST *p_section;
562 int64_t section_index;
563 int32_t aid_location;
564 char stitle[BBS_section_title_max_len + 1];
565 char master_list[(BBS_username_max_len + 1) * 3 + 1];
566 char page_info_str[LINE_BUFFER_LEN];
567 ARTICLE *p_articles[BBS_article_limit_per_page];
568 int article_count;
569 int page_count;
570 int ontop_start_offset;
571 int page_id = 0;
572 int selected_index = 0;
573 ARTICLE_CACHE cache;
574 int ret;
575 int loop;
576 int direction;
577 ARTICLE article_new;
578 int page_id_cur;
579 const ARTICLE *p_article_locate;
580 USER_INFO user_info;
581 char user_intro[BBS_user_intro_max_len];
582 char username[BBS_username_max_len + 1];
583 char username_list[1][BBS_username_max_len + 1];
584 int32_t uid;
585 int i;
586 int ok;
587
588 p_section = section_list_find_by_name(sname);
589 if (p_section == NULL)
590 {
591 log_error("Section %s not found\n", sname);
592 return -1;
593 }
594
595 if (!checkpriv(&BBS_priv, p_section->sid, S_LIST))
596 {
597 log_error("Forbid access to unauthorized section, sid=%d, uid=%d\n",
598 p_section->sid, BBS_priv.uid);
599 return -1;
600 }
601
602 section_index = get_section_index(p_section);
603
604 if (get_section_info(p_section, NULL, stitle, master_list) < 0)
605 {
606 log_error("get_section_info(sid=%d) error\n", p_section->sid);
607 return -4;
608 }
609
610 if (aid == 0)
611 {
612 aid_location = section_aid_locations[section_index];
613 }
614 else
615 {
616 aid_location = aid;
617 }
618
619 // Locate at article with aid_locate
620 if (aid_location > 0)
621 {
622 p_article_locate = article_block_find_by_aid(aid_location);
623 if (p_article_locate == NULL)
624 {
625 log_error("article_block_find_by_aid(%d) error\n", aid_location);
626 return -3;
627 }
628
629 ret = locate_article_in_section(p_section, p_article_locate, 0, 0,
630 &page_id, &selected_index, &article_count);
631 if (ret < 0)
632 {
633 log_error("locate_article_in_section(sid=%d, aid=%d, direction=0, step=0) error\n",
634 p_section->sid, p_article_locate->aid);
635 return -3;
636 }
637 }
638
639 if (section_list_draw_screen(sname, stitle, master_list, display_nickname) < 0)
640 {
641 log_error("section_list_draw_screen() error\n");
642 return -2;
643 }
644
645 ret = query_section_articles(p_section, page_id, p_articles, &article_count, &page_count, &ontop_start_offset);
646 if (ret < 0)
647 {
648 log_error("query_section_articles(sid=%d, page_id=%d) error\n", p_section->sid, page_id);
649 return -3;
650 }
651
652 section_topic_view_tid = -1;
653
654 if (article_count == 0) // empty section
655 {
656 selected_index = 0;
657 }
658 else if (aid > 0)
659 {
660 // Update current topic
661 section_topic_view_tid = (p_articles[selected_index]->tid == 0 ? p_articles[selected_index]->aid : p_articles[selected_index]->tid);
662
663 // Update current aid location
664 section_aid_locations[section_index] = p_articles[selected_index]->aid;
665 }
666
667 while (!SYS_server_exit)
668 {
669 ret = section_list_draw_items(page_id, p_articles, article_count, display_nickname, ontop_start_offset);
670 if (ret < 0)
671 {
672 log_error("section_list_draw_items(sid=%d, page_id=%d) error\n", p_section->sid, page_id);
673 return -4;
674 }
675
676 snprintf(page_info_str, sizeof(page_info_str),
677 "\033[33m[第\033[36m%d\033[33m/\033[36m%d\033[33m页]",
678 page_id + 1, MAX(page_count, 1));
679
680 show_bottom(page_info_str);
681 iflush();
682
683 if (user_online_update(sname) < 0)
684 {
685 log_error("user_online_update(%s) error\n", sname);
686 }
687
688 ret = section_list_select(page_count, article_count, &page_id, &selected_index);
689
690 switch (ret)
691 {
692 case EXIT_SECTION:
693 // Update current aid location
694 if (p_articles[selected_index] != NULL)
695 {
696 section_aid_locations[section_index] = p_articles[selected_index]->aid;
697 }
698 else
699 {
700 log_error("p_articles[selected_index=%d] is NULL when exit section [%s]\n", selected_index, sname);
701 }
702 return 0;
703 case CHANGE_PAGE:
704 ret = query_section_articles(p_section, page_id, p_articles, &article_count, &page_count, &ontop_start_offset);
705 if (ret < 0)
706 {
707 log_error("query_section_articles(sid=%d, page_id=%d) error\n", p_section->sid, page_id);
708 return -3;
709 }
710 if (article_count == 0) // empty section
711 {
712 selected_index = 0;
713 }
714 else if (selected_index >= article_count)
715 {
716 selected_index = article_count - 1;
717 }
718 break;
719 case VIEW_ARTICLE:
720 do
721 {
722 loop = 0;
723
724 if (article_cache_load(&cache, VAR_ARTICLE_CACHE_DIR, p_articles[selected_index]) < 0)
725 {
726 log_error("article_cache_load(aid=%d, cid=%d) error\n", p_articles[selected_index]->aid, p_articles[selected_index]->cid);
727 break;
728 }
729
730 if (user_online_update("VIEW_ARTICLE") < 0)
731 {
732 log_error("user_online_update(VIEW_ARTICLE) error\n");
733 }
734
735 ret = display_data(cache.p_data, cache.line_total, cache.line_offsets, 0,
736 display_article_key_handler, DATA_READ_HELP);
737
738 if (article_cache_unload(&cache) < 0)
739 {
740 log_error("article_cache_unload(aid=%d, cid=%d) error\n", p_articles[selected_index]->aid, p_articles[selected_index]->cid);
741 break;
742 }
743
744 // Update article_view_log
745 if (article_view_log_set_viewed(p_articles[selected_index]->aid, &BBS_article_view_log) < 0)
746 {
747 log_error("article_view_log_set_viewed(aid=%d) error\n", p_articles[selected_index]->aid);
748 }
749
750 switch (ret)
751 {
752 case KEY_UP:
753 if (selected_index <= 0)
754 {
755 if (page_id > 0)
756 {
757 page_id--;
758 selected_index = BBS_article_limit_per_page - 1;
759
760 ret = query_section_articles(p_section, page_id, p_articles, &article_count, &page_count, &ontop_start_offset);
761 if (ret < 0)
762 {
763 log_error("query_section_articles(sid=%d, page_id=%d) error\n", p_section->sid, page_id);
764 return -3;
765 }
766
767 if (article_count != BBS_article_limit_per_page) // page is not full
768 {
769 selected_index = MAX(0, article_count - 1);
770 }
771 else
772 {
773 loop = 1;
774 }
775 }
776 }
777 else
778 {
779 selected_index--;
780 loop = 1;
781 }
782 break;
783 case KEY_DOWN:
784 if (selected_index + 1 >= article_count) // next page
785 {
786 if (page_id + 1 < page_count)
787 {
788 page_id++;
789 selected_index = 0;
790
791 ret = query_section_articles(p_section, page_id, p_articles, &article_count, &page_count, &ontop_start_offset);
792 if (ret < 0)
793 {
794 log_error("query_section_articles(sid=%d, page_id=%d) error\n", p_section->sid, page_id);
795 return -3;
796 }
797
798 if (article_count == 0) // empty page
799 {
800 selected_index = 0;
801 }
802 else
803 {
804 loop = 1;
805 }
806 }
807 }
808 else
809 {
810 selected_index++;
811 loop = 1;
812 }
813 break;
814 case KEY_PGUP:
815 case KEY_PGDN:
816 direction = (ret == KEY_PGUP ? -1 : 1);
817 ret = locate_article_in_section(p_section, p_articles[selected_index], direction, 1,
818 &page_id, &selected_index, &article_count);
819 if (ret < 0)
820 {
821 log_error("locate_article_in_section(sid=%d, aid=%d, direction=%d, step=1) error\n",
822 p_section->sid, p_articles[selected_index]->aid, direction);
823 return -3;
824 }
825 else if (ret > 0) // found
826 {
827 ret = query_section_articles(p_section, page_id, p_articles, &article_count, &page_count, &ontop_start_offset);
828 if (ret < 0)
829 {
830 log_error("query_section_articles(sid=%d, page_id=%d) error\n", p_section->sid, page_id);
831 return -3;
832 }
833 loop = 1;
834 }
835 break;
836 case 'r': // Reply article
837 if (user_online_update("REPLY_ARTICLE") < 0)
838 {
839 log_error("user_online_update(REPLY_ARTICLE) error\n");
840 }
841
842 if (article_reply(p_section, p_articles[selected_index], &article_new) < 0)
843 {
844 log_error("article_reply(aid=%d) error\n", p_articles[selected_index]->aid);
845 }
846 loop = 1;
847 break;
848 case '=': // First topic article
849 case '\\': // Last topic article
850 page_id_cur = page_id;
851 direction = (ret == '=' ? -1 : 1);
852 ret = locate_article_in_section(p_section, p_articles[selected_index], direction, BBS_article_limit_per_section,
853 &page_id, &selected_index, &article_count);
854 if (ret < 0)
855 {
856 log_error("locate_article_in_section(sid=%d, aid=%d, direction=%d, step=%d) error\n",
857 p_section->sid, p_articles[selected_index]->aid, direction, BBS_article_limit_per_section);
858 return -3;
859 }
860 else if (ret > 0) // found
861 {
862 if (page_id != page_id_cur) // page changed
863 {
864 ret = query_section_articles(p_section, page_id, p_articles, &article_count, &page_count, &ontop_start_offset);
865 if (ret < 0)
866 {
867 log_error("query_section_articles(sid=%d, page_id=%d) error\n", p_section->sid, page_id);
868 return -3;
869 }
870 }
871 loop = 1;
872 }
873 break;
874 }
875 } while (loop);
876
877 // Update current topic
878 section_topic_view_tid = (p_articles[selected_index]->tid == 0 ? p_articles[selected_index]->aid : p_articles[selected_index]->tid);
879
880 if (section_list_draw_screen(sname, stitle, master_list, display_nickname) < 0)
881 {
882 log_error("section_list_draw_screen() error\n");
883 return -2;
884 }
885 break;
886 case CHANGE_NAME_DISPLAY:
887 display_nickname = !display_nickname;
888 if (section_list_draw_screen(sname, stitle, master_list, display_nickname) < 0)
889 {
890 log_error("section_list_draw_screen() error\n");
891 return -2;
892 }
893 break;
894 case POST_ARTICLE:
895 if (user_online_update("POST_ARTICLE") < 0)
896 {
897 log_error("user_online_update(POST_ARTICLE) error\n");
898 }
899
900 if ((ret = article_post(p_section, &article_new)) < 0)
901 {
902 log_error("article_post(sid=%d) error\n", p_section->sid);
903 }
904 else if (ret > 0) // New article posted
905 {
906 ret = query_section_articles(p_section, page_id, p_articles, &article_count, &page_count, &ontop_start_offset);
907 if (ret < 0)
908 {
909 log_error("query_section_articles(sid=%d, page_id=%d) error\n", p_section->sid, page_id);
910 return -3;
911 }
912 }
913 if (section_list_draw_screen(sname, stitle, master_list, display_nickname) < 0)
914 {
915 log_error("section_list_draw_screen() error\n");
916 return -2;
917 }
918 break;
919 case EDIT_ARTICLE:
920 if (!checkpriv(&BBS_priv, p_section->sid, S_POST) ||
921 p_articles[selected_index]->uid != BBS_priv.uid)
922 {
923 break; // No permission
924 }
925
926 if (user_online_update("EDIT_ARTICLE") < 0)
927 {
928 log_error("user_online_update() error\n");
929 }
930
931 if (article_modify(p_section, p_articles[selected_index], &article_new) < 0)
932 {
933 log_error("article_modify(aid=%d) error\n", p_articles[selected_index]->aid);
934 }
935 if (section_list_draw_screen(sname, stitle, master_list, display_nickname) < 0)
936 {
937 log_error("section_list_draw_screen() error\n");
938 return -2;
939 }
940 break;
941 case DELETE_ARTICLE:
942 if (!checkpriv(&BBS_priv, p_section->sid, S_POST) ||
943 (!checkpriv(&BBS_priv, p_section->sid, S_MAN_S) && p_articles[selected_index]->uid != BBS_priv.uid))
944 {
945 break; // No permission
946 }
947 if ((ret = article_del(p_section, p_articles[selected_index])) < 0)
948 {
949 log_error("article_del(aid=%d) error\n", p_articles[selected_index]->aid);
950 }
951 else if (ret > 0) // Article deleted
952 {
953 ret = query_section_articles(p_section, page_id, p_articles, &article_count, &page_count, &ontop_start_offset);
954 if (ret < 0)
955 {
956 log_error("query_section_articles(sid=%d, page_id=%d) error\n", p_section->sid, page_id);
957 return -3;
958 }
959 }
960 if (section_list_draw_screen(sname, stitle, master_list, display_nickname) < 0)
961 {
962 log_error("section_list_draw_screen() error\n");
963 return -2;
964 }
965 break;
966 case QUERY_ARTICLE:
967 if ((ret = display_article_meta(p_articles[selected_index]->aid)) < 0)
968 {
969 log_error("display_article_meta(aid=%d) error\n", p_articles[selected_index]->aid);
970 }
971 if (section_list_draw_screen(sname, stitle, master_list, display_nickname) < 0)
972 {
973 log_error("section_list_draw_screen() error\n");
974 return -2;
975 }
976 break;
977 case QUERY_USER:
978 if ((ret = query_user_info_by_uid(p_articles[selected_index]->uid, &user_info, user_intro, sizeof(user_intro))) < 0)
979 {
980 log_error("query_user_info_by_uid(uid=%d) error\n", p_articles[selected_index]->uid);
981 return -2;
982 }
983 else if (ret == 0)
984 {
985 clearscr();
986 prints("该用户已升天");
987 press_any_key();
988 }
989 else if (user_info_display(&user_info) < 0) // && ret > 0
990 {
991 log_error("user_info_display(uid=%d) error\n", p_articles[selected_index]->uid);
992 }
993
994 if (section_list_draw_screen(sname, stitle, master_list, display_nickname) < 0)
995 {
996 log_error("section_list_draw_screen() error\n");
997 return -2;
998 }
999 break;
1000 case SET_FAVOR_ARTICLE:
1001 ret = article_favor_set(p_articles[selected_index]->tid == 0
1002 ? p_articles[selected_index]->aid
1003 : p_articles[selected_index]->tid,
1004 &BBS_article_favor, 1);
1005 if (ret < 0)
1006 {
1007 log_error("article_favor_set(aid=%d, 1) error\n",
1008 p_articles[selected_index]->tid == 0 ? p_articles[selected_index]->aid : p_articles[selected_index]->tid);
1009 }
1010 break;
1011 case UNSET_FAVOR_ARTICLE:
1012 ret = article_favor_set(p_articles[selected_index]->tid == 0
1013 ? p_articles[selected_index]->aid
1014 : p_articles[selected_index]->tid,
1015 &BBS_article_favor, 0);
1016 if (ret < 0)
1017 {
1018 log_error("article_favor_set(aid=%d, 0) error\n",
1019 p_articles[selected_index]->tid == 0 ? p_articles[selected_index]->aid : p_articles[selected_index]->tid);
1020 }
1021 break;
1022 case FIRST_TOPIC_ARTICLE:
1023 case LAST_TOPIC_ARTICLE:
1024 page_id_cur = page_id;
1025 direction = (ret == FIRST_TOPIC_ARTICLE ? -1 : 1);
1026 ret = locate_article_in_section(p_section, p_articles[selected_index], direction, BBS_article_limit_per_section,
1027 &page_id, &selected_index, &article_count);
1028 if (ret < 0)
1029 {
1030 log_error("locate_article_in_section(sid=%d, aid=%d, direction=%d, step=%d) error\n",
1031 p_section->sid, p_articles[selected_index]->aid, direction, BBS_article_limit_per_section);
1032 return -3;
1033 }
1034 else if (ret > 0 && page_id != page_id_cur) // found and page changed
1035 {
1036 ret = query_section_articles(p_section, page_id, p_articles, &article_count, &page_count, &ontop_start_offset);
1037 if (ret < 0)
1038 {
1039 log_error("query_section_articles(sid=%d, page_id=%d) error\n", p_section->sid, page_id);
1040 return -3;
1041 }
1042 }
1043 break;
1044 case SCAN_NEW_ARTICLE:
1045 ret = scan_unread_article_in_section(p_section, p_articles[selected_index], &p_article_locate);
1046 if (ret < 0)
1047 {
1048 log_error("scan_unread_article_in_section(sid=%d, aid=%d) error\n",
1049 p_section->sid, p_articles[selected_index]->aid);
1050 return -3;
1051 }
1052 else if (ret == 0) // not found
1053 {
1054 break;
1055 }
1056 page_id_cur = page_id;
1057 ret = locate_article_in_section(p_section, p_article_locate, 0, 0,
1058 &page_id, &selected_index, &article_count);
1059 if (ret < 0)
1060 {
1061 log_error("locate_article_in_section(sid=%d, aid=%d, direction=0, step=0) error\n",
1062 p_section->sid, p_article_locate->aid);
1063 return -3;
1064 }
1065 else if (ret > 0 && page_id != page_id_cur) // found and page changed
1066 {
1067 ret = query_section_articles(p_section, page_id, p_articles, &article_count, &page_count, &ontop_start_offset);
1068 if (ret < 0)
1069 {
1070 log_error("query_section_articles(sid=%d, page_id=%d) error\n", p_section->sid, page_id);
1071 return -3;
1072 }
1073 }
1074 break;
1075 case SCAN_ARTICLE_BACKWARD_BY_USER:
1076 case SCAN_ARTICLE_FORWARD_BY_USER:
1077 direction = (ret == SCAN_ARTICLE_FORWARD_BY_USER ? 1 : -1);
1078 strncpy(username, p_articles[selected_index]->username, sizeof(username) - 1);
1079 username[sizeof(username) - 1] = '\0';
1080 uid = 0;
1081
1082 moveto(SCREEN_ROWS, 1);
1083 clrtoeol();
1084 get_data(SCREEN_ROWS, 1,
1085 (direction == 1 ? "向下搜寻作者: " : "向上搜寻作者: "),
1086 username, sizeof(username), BBS_username_max_len);
1087
1088 if (username[0] == '\0')
1089 {
1090 break;
1091 }
1092
1093 // Verify format
1094 for (i = 0, ok = 1; ok && username[i] != '\0'; i++)
1095 {
1096 if (!(isalpha(username[i]) || (i > 0 && (isdigit(username[i]) || username[i] == '_'))))
1097 {
1098 ok = 0;
1099 }
1100 }
1101 if (ok && i > BBS_username_max_len)
1102 {
1103 ok = 0;
1104 }
1105 if (!ok)
1106 {
1107 break;
1108 }
1109
1110 ret = query_user_info_by_username(username, 1, &uid, username_list);
1111 if (ret < 0)
1112 {
1113 log_error("query_user_info_by_username(%s) error\n", username);
1114 break;
1115 }
1116
1117 if (uid > 0)
1118 {
1119 ret = scan_article_in_section_by_uid(p_section, p_articles[selected_index],
1120 direction, uid, &p_article_locate);
1121 if (ret < 0)
1122 {
1123 log_error("scan_article_in_section_by_uid(sid=%d, aid=%d, direction=%d, uid=%d) error\n",
1124 p_section->sid, p_articles[selected_index]->aid, direction, uid);
1125 return -3;
1126 }
1127 else if (ret == 0) // not found
1128 {
1129 break;
1130 }
1131 }
1132 else // uid == 0
1133 {
1134 ret = scan_article_in_section_by_username(p_section, p_articles[selected_index],
1135 direction, username, &p_article_locate);
1136 if (ret < 0)
1137 {
1138 log_error("scan_article_in_section_by_username(sid=%d, aid=%d, direction=%d, username=%s) error\n",
1139 p_section->sid, p_articles[selected_index]->aid, direction, username);
1140 return -3;
1141 }
1142 else if (ret == 0) // not found
1143 {
1144 break;
1145 }
1146 }
1147
1148 page_id_cur = page_id;
1149 ret = locate_article_in_section(p_section, p_article_locate, 0, 0,
1150 &page_id, &selected_index, &article_count);
1151 if (ret < 0)
1152 {
1153 log_error("locate_article_in_section(sid=%d, aid=%d, direction=0, step=0) error\n",
1154 p_section->sid, p_article_locate->aid);
1155 return -3;
1156 }
1157 else if (ret > 0 && page_id != page_id_cur) // found and page changed
1158 {
1159 ret = query_section_articles(p_section, page_id, p_articles, &article_count, &page_count, &ontop_start_offset);
1160 if (ret < 0)
1161 {
1162 log_error("query_section_articles(sid=%d, page_id=%d) error\n", p_section->sid, page_id);
1163 return -3;
1164 }
1165 }
1166 break;
1167 case SEARCH_USER:
1168 user_list_search();
1169 if (section_list_draw_screen(sname, stitle, master_list, display_nickname) < 0)
1170 {
1171 log_error("section_list_draw_screen() error\n");
1172 return -2;
1173 }
1174 break;
1175 case SHOW_HELP:
1176 // Display help information
1177 display_file(DATA_READ_HELP, 1);
1178 if (section_list_draw_screen(sname, stitle, master_list, display_nickname) < 0)
1179 {
1180 log_error("section_list_draw_screen() error\n");
1181 return -2;
1182 }
1183 break;
1184 case VIEW_EX_DIR:
1185 if (section_list_ex_dir_display(p_section) < 0)
1186 {
1187 log_error("section_list_ex_dir_display(sid=%d) error\n", p_section->sid);
1188 }
1189 if (section_list_draw_screen(sname, stitle, master_list, display_nickname) < 0)
1190 {
1191 log_error("section_list_draw_screen() error\n");
1192 return -2;
1193 }
1194 break;
1195 case SHOW_TOP10:
1196 show_top10_menu(NULL);
1197 if (section_list_draw_screen(sname, stitle, master_list, display_nickname) < 0)
1198 {
1199 log_error("section_list_draw_screen() error\n");
1200 return -2;
1201 }
1202 break;
1203 default:
1204 log_error("Unknown command %d\n", ret);
1205 }
1206 }
1207
1208 return 0;
1209 }
1210
1211 int section_list_ex_dir_display(SECTION_LIST *p_section)
1212 {
1213 MENU_SET ex_menu_set;
1214 int ch = 0;
1215
1216 if (p_section == NULL)
1217 {
1218 log_error("NULL pointer error\n");
1219 return -1;
1220 }
1221
1222 if (p_section->ex_menu_tm == 0) // N/A
1223 {
1224 moveto(2, 1);
1225 clrtoeol();
1226 prints("该版块精华区未开放");
1227 press_any_key();
1228 return 0;
1229 }
1230
1231 if (get_section_ex_menu_set(p_section, &ex_menu_set) < 0)
1232 {
1233 log_error("get_section_ex_menu_set(sid=%d) error\n", p_section->sid);
1234 return -3;
1235 }
1236 if (get_menu_shm_readonly(&ex_menu_set) < 0)
1237 {
1238 log_error("get_menu_shm_readonly(sid=%d) error\n", p_section->sid);
1239 return -3;
1240 }
1241
1242 clearscr();
1243 show_bottom("");
1244
1245 if (display_menu(&ex_menu_set) == 0)
1246 {
1247 while (!SYS_server_exit)
1248 {
1249 iflush();
1250 ch = igetch(100);
1251
1252 if (ch != KEY_NULL && ch != KEY_TIMEOUT)
1253 {
1254 BBS_last_access_tm = time(NULL);
1255 }
1256
1257 switch (ch)
1258 {
1259 case KEY_NULL: // broken pipe
1260 log_error("KEY_NULL\n");
1261 return 0;
1262 case KEY_TIMEOUT:
1263 if (time(NULL) - BBS_last_access_tm >= MAX_DELAY_TIME)
1264 {
1265 log_error("User input timeout\n");
1266 return 0;
1267 }
1268 continue;
1269 case CR:
1270 default:
1271 switch (menu_control(&ex_menu_set, ch))
1272 {
1273 case EXITMENU:
1274 ch = EXITMENU;
1275 break;
1276 case REDRAW:
1277 clearscr();
1278 show_bottom("");
1279 display_menu(&ex_menu_set);
1280 break;
1281 case NOREDRAW:
1282 case UNKNOWN_CMD:
1283 default:
1284 break;
1285 }
1286 }
1287
1288 if (ch == EXITMENU)
1289 {
1290 break;
1291 }
1292 }
1293 }
1294
1295 detach_menu_shm(&ex_menu_set);
1296
1297 return 0;
1298 }
1299
1300 int section_aid_locations_save(int uid)
1301 {
1302 char filename[FILE_PATH_LEN];
1303 FILE *fp;
1304 int i;
1305 int ret = 0;
1306
1307 snprintf(filename, sizeof(filename), "%s/%d", VAR_SECTION_AID_LOC_DIR, uid);
1308
1309 if ((fp = fopen(filename, "wb")) == NULL)
1310 {
1311 log_error("fopen(%s, wb) error: %d\n", filename, errno);
1312 return -1;
1313 }
1314
1315 for (i = 0; i < p_section_list_pool->section_count; i++)
1316 {
1317 if (fwrite(&(p_section_list_pool->sections[i].sid), sizeof(p_section_list_pool->sections[i].sid), 1, fp) != 1)
1318 {
1319 log_error("fwrite(%s, sid) error\n", filename);
1320 ret = -2;
1321 break;
1322 }
1323
1324 if (fwrite(&(section_aid_locations[i]), sizeof(section_aid_locations[i]), 1, fp) != 1)
1325 {
1326 log_error("fwrite(%s, aid) error\n", filename);
1327 ret = -2;
1328 break;
1329 }
1330 }
1331
1332 if (fclose(fp) < 0)
1333 {
1334 log_error("fclose(%s) error: %d\n", filename, errno);
1335 ret = -1;
1336 }
1337
1338 return ret;
1339 }
1340
1341 int section_aid_locations_load(int uid)
1342 {
1343 char filename[FILE_PATH_LEN];
1344 FILE *fp;
1345 int i;
1346 int32_t sid;
1347 int32_t aid;
1348 SECTION_LIST *p_section;
1349 int ret = 0;
1350
1351 snprintf(filename, sizeof(filename), "%s/%d", VAR_SECTION_AID_LOC_DIR, uid);
1352
1353 if ((fp = fopen(filename, "rb")) == NULL)
1354 {
1355 if (errno == ENOENT) // file not exist
1356 {
1357 return 0;
1358 }
1359 log_error("fopen(%s, rb) error: %d\n", filename, errno);
1360 return -1;
1361 }
1362
1363 while (!feof(fp))
1364 {
1365 if (fread(&sid, sizeof(sid), 1, fp) != 1)
1366 {
1367 if (ferror(fp) == 0)
1368 {
1369 break;
1370 }
1371 log_error("fread(%s, sid) error: %d\n", filename, ferror(fp));
1372 ret = -2;
1373 break;
1374 }
1375
1376 if (fread(&aid, sizeof(aid), 1, fp) != 1)
1377 {
1378 if (ferror(fp) == 0)
1379 {
1380 break;
1381 }
1382 log_error("fread(%s, aid) error: %d\n", filename, ferror(fp));
1383 ret = -2;
1384 break;
1385 }
1386
1387 p_section = section_list_find_by_sid(sid);
1388 if (p_section == NULL)
1389 {
1390 continue; // skip section no longer exist
1391 }
1392
1393 i = get_section_index(p_section);
1394 if (i < 0)
1395 {
1396 log_error("get_section_index(sid=%d) error\n", sid);
1397 ret = -3;
1398 break;
1399 }
1400 section_aid_locations[i] = aid;
1401 }
1402
1403 if (fclose(fp) < 0)
1404 {
1405 log_error("fclose(%s) error: %d\n", filename, errno);
1406 ret = -1;
1407 }
1408
1409 return ret;
1410 }

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