/[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.64 - (show annotations)
Mon Nov 3 06:43:55 2025 UTC (4 months, 1 week ago) by sysadm
Branch: MAIN
Changes since 1.63: +111 -0 lines
Content type: text/x-csrc
Add scan_article_in_section_by_uid() and scan_article_in_section_by_username()
Support search prior / next article of the user matching the specific name prefix

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

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