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

Contents of /lbbs/src/section_list.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.11 - (show annotations)
Fri May 23 07:06:57 2025 UTC (9 months, 3 weeks ago) by sysadm
Branch: MAIN
Changes since 1.10: +224 -16 lines
Content type: text/x-csrc
Add section_list_move_topic()

1 /***************************************************************************
2 section_list.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 "section_list.h"
18 #include "log.h"
19 #include "trie_dict.h"
20 #include <stdio.h>
21 #include <string.h>
22 #include <signal.h>
23 #include <unistd.h>
24 #include <stdlib.h>
25 #include <errno.h>
26 #include <sys/param.h>
27 #include <sys/shm.h>
28 #include <sys/ipc.h>
29
30 #define ARTICLE_BLOCK_PER_SHM 400 // sizeof(ARTICLE_BLOCK) * ARTICLE_BLOCK_PER_SHM is the size of each shm segment to allocate
31 #define ARTICLE_BLOCK_SHM_COUNT_LIMIT 256 // limited by length (8-bit) of proj_id in ftok(path, proj_id)
32 #define ARTICLE_BLOCK_PER_POOL (ARTICLE_BLOCK_PER_SHM * ARTICLE_BLOCK_SHM_COUNT_LIMIT)
33
34 struct article_block_t
35 {
36 ARTICLE articles[ARTICLE_PER_BLOCK];
37 int32_t article_count;
38 struct article_block_t *p_next_block;
39 };
40 typedef struct article_block_t ARTICLE_BLOCK;
41
42 struct article_block_shm_t
43 {
44 int shmid;
45 void *p_shm;
46 };
47 typedef struct article_block_shm_t ARTICLE_BLOCK_SHM;
48
49 struct article_block_pool_t
50 {
51 ARTICLE_BLOCK_SHM shm_pool[ARTICLE_BLOCK_SHM_COUNT_LIMIT];
52 int shm_count;
53 ARTICLE_BLOCK *p_block_free_list;
54 ARTICLE_BLOCK *p_block[ARTICLE_BLOCK_PER_POOL];
55 int32_t block_count;
56 };
57 typedef struct article_block_pool_t ARTICLE_BLOCK_POOL;
58
59 static ARTICLE_BLOCK_POOL *p_article_block_pool = NULL;
60
61 static SECTION_LIST *p_section_list_pool = NULL;
62 static int section_list_count = 0;
63 static TRIE_NODE *p_trie_dict_section_list = NULL;
64
65 int article_block_init(const char *filename, int block_count)
66 {
67 int shmid;
68 int proj_id;
69 key_t key;
70 size_t size;
71 void *p_shm;
72 int i;
73 int block_count_in_shm;
74 ARTICLE_BLOCK *p_block_in_shm;
75 ARTICLE_BLOCK **pp_block_next;
76
77 if (p_article_block_pool != NULL)
78 {
79 log_error("article_block_pool already initialized\n");
80 return -1;
81 }
82
83 if (block_count > ARTICLE_BLOCK_PER_POOL)
84 {
85 log_error("article_block_count exceed limit %d\n", ARTICLE_BLOCK_PER_POOL);
86 return -2;
87 }
88
89 p_article_block_pool = calloc(1, sizeof(ARTICLE_BLOCK_POOL));
90 if (p_article_block_pool == NULL)
91 {
92 log_error("calloc(ARTICLE_BLOCK_POOL) OOM\n");
93 return -2;
94 }
95
96 // Allocate shared memory
97 p_article_block_pool->shm_count = 0;
98 pp_block_next = &(p_article_block_pool->p_block_free_list);
99
100 while (block_count > 0)
101 {
102 block_count_in_shm = MIN(block_count, ARTICLE_BLOCK_PER_SHM);
103 block_count -= block_count_in_shm;
104
105 proj_id = getpid() + p_article_block_pool->shm_count;
106 key = ftok(filename, proj_id);
107 if (key == -1)
108 {
109 log_error("ftok(%s, %d) error (%d)\n", filename, proj_id, errno);
110 article_block_cleanup();
111 return -3;
112 }
113
114 size = sizeof(shmid) + sizeof(ARTICLE_BLOCK) * (size_t)block_count_in_shm;
115 shmid = shmget(key, size, IPC_CREAT | IPC_EXCL | 0600);
116 if (shmid == -1)
117 {
118 log_error("shmget(shm_index = %d, size = %d) error (%d)\n", p_article_block_pool->shm_count, size, errno);
119 article_block_cleanup();
120 return -3;
121 }
122 p_shm = shmat(shmid, NULL, 0);
123 if (p_shm == (void *)-1)
124 {
125 log_error("shmat(shmid = %d) error (%d)\n", shmid, errno);
126 article_block_cleanup();
127 return -3;
128 }
129
130 (p_article_block_pool->shm_pool + p_article_block_pool->shm_count)->shmid = shmid;
131 (p_article_block_pool->shm_pool + p_article_block_pool->shm_count)->p_shm = p_shm;
132 p_article_block_pool->shm_count++;
133
134 p_block_in_shm = p_shm;
135 *pp_block_next = p_block_in_shm;
136
137 for (i = 0; i < block_count_in_shm; i++)
138 {
139 if (i < block_count_in_shm - 1)
140 {
141 (p_block_in_shm + i)->p_next_block = (p_block_in_shm + i + 1);
142 }
143 else
144 {
145 (p_block_in_shm + i)->p_next_block = NULL;
146 pp_block_next = &((p_block_in_shm + i)->p_next_block);
147 }
148 }
149 }
150
151 p_article_block_pool->block_count = 0;
152
153 return 0;
154 }
155
156 void article_block_cleanup(void)
157 {
158 if (p_article_block_pool != NULL)
159 {
160 for (int i = 0; i < p_article_block_pool->shm_count; i++)
161 {
162 if (shmdt((p_article_block_pool->shm_pool + i)->p_shm) == -1)
163 {
164 log_error("shmdt(shmid = %d) error (%d)\n", (p_article_block_pool->shm_pool + i)->shmid, errno);
165 }
166
167 if (shmctl((p_article_block_pool->shm_pool + i)->shmid, IPC_RMID, NULL) == -1)
168 {
169 log_error("shmctl(shmid = %d, IPC_RMID) error (%d)\n", (p_article_block_pool->shm_pool + i)->shmid, errno);
170 }
171 }
172
173 free(p_article_block_pool);
174 p_article_block_pool = NULL;
175 }
176 }
177
178 inline static ARTICLE_BLOCK *pop_free_article_block(void)
179 {
180 ARTICLE_BLOCK *p_block = NULL;
181
182 if (p_article_block_pool->p_block_free_list != NULL)
183 {
184 p_block = p_article_block_pool->p_block_free_list;
185 p_article_block_pool->p_block_free_list = p_block->p_next_block;
186 p_block->p_next_block = NULL;
187 p_block->article_count = 0;
188 }
189
190 return p_block;
191 }
192
193 inline static void push_free_article_block(ARTICLE_BLOCK *p_block)
194 {
195 p_block->p_next_block = p_article_block_pool->p_block_free_list;
196 p_article_block_pool->p_block_free_list = p_block;
197 }
198
199 int article_block_reset(void)
200 {
201 ARTICLE_BLOCK *p_block;
202
203 if (p_article_block_pool == NULL)
204 {
205 log_error("article_block_pool not initialized\n");
206 return -1;
207 }
208
209 while (p_article_block_pool->block_count > 0)
210 {
211 p_article_block_pool->block_count--;
212 p_block = p_article_block_pool->p_block[p_article_block_pool->block_count];
213 push_free_article_block(p_block);
214 }
215
216 return 0;
217 }
218
219 ARTICLE *article_block_find_by_aid(int32_t aid)
220 {
221 ARTICLE_BLOCK *p_block;
222 int left;
223 int right;
224 int mid;
225
226 if (p_article_block_pool == NULL)
227 {
228 log_error("article_block_pool not initialized\n");
229 return NULL;
230 }
231
232 if (p_article_block_pool->block_count == 0) // empty
233 {
234 return NULL;
235 }
236
237 left = 0;
238 right = p_article_block_pool->block_count;
239
240 // aid in the range [ head aid of blocks[left], tail aid of blocks[right - 1] ]
241 while (left < right - 1)
242 {
243 // get block offset no less than mid value of left and right block offsets
244 mid = (left + right) / 2 + (right - left) % 2;
245
246 if (mid >= p_article_block_pool->block_count)
247 {
248 log_error("block(mid = %d) is out of boundary\n", mid);
249 return NULL;
250 }
251
252 if (aid < p_article_block_pool->p_block[mid]->articles[0].aid)
253 {
254 right = mid;
255 }
256 else
257 {
258 left = mid;
259 }
260 }
261
262 p_block = p_article_block_pool->p_block[left];
263
264 left = 0;
265 right = p_block->article_count - 1;
266
267 // aid in the range [ aid of articles[left], aid of articles[right] ]
268 while (left < right)
269 {
270 mid = (left + right) / 2;
271
272 if (aid <= p_block->articles[mid].aid)
273 {
274 right = mid;
275 }
276 else
277 {
278 left = mid + 1;
279 }
280 }
281
282 return (p_block->articles + left);
283 }
284
285 ARTICLE *article_block_find_by_index(int index)
286 {
287 ARTICLE_BLOCK *p_block;
288
289 if (p_article_block_pool == NULL)
290 {
291 log_error("article_block_pool not initialized\n");
292 return NULL;
293 }
294
295 if (index < 0 || index / ARTICLE_PER_BLOCK >= p_article_block_pool->block_count)
296 {
297 log_error("section_data_find_article_by_index(%d) is out of boundary of block [0, %d)\n", index, p_article_block_pool->block_count);
298 return NULL;
299 }
300
301 p_block = p_article_block_pool->p_block[index / ARTICLE_PER_BLOCK];
302
303 if (index % ARTICLE_PER_BLOCK >= p_block->article_count)
304 {
305 log_error("section_data_find_article_by_index(%d) is out of boundary of article [0, %d)\n", index, p_block->article_count);
306 return NULL;
307 }
308
309 return (p_block->articles + (index % ARTICLE_PER_BLOCK));
310 }
311
312 SECTION_LIST *section_list_create(int32_t sid, const char *sname, const char *stitle, const char *master_name)
313 {
314 SECTION_LIST *p_section;
315
316 if (p_section_list_pool == NULL)
317 {
318 p_section_list_pool = calloc(BBS_max_section, sizeof(SECTION_LIST));
319 if (p_section_list_pool == NULL)
320 {
321 log_error("calloc(%d SECTION_LIST) OOM\n", BBS_max_section);
322 return NULL;
323 }
324
325 section_list_count = 0;
326 }
327
328 if (p_trie_dict_section_list == NULL)
329 {
330 p_trie_dict_section_list = trie_dict_create();
331 if (p_trie_dict_section_list == NULL)
332 {
333 log_error("trie_dict_create() OOM\n", BBS_max_section);
334 return NULL;
335 }
336 }
337
338 if (section_list_count >= BBS_max_section)
339 {
340 log_error("section_list_count exceed limit %d\n", BBS_max_section);
341 return NULL;
342 }
343
344 p_section = p_section_list_pool + section_list_count;
345
346 p_section->sid = sid;
347
348 strncpy(p_section->sname, sname, sizeof(p_section->sname - 1));
349 p_section->sname[sizeof(p_section->sname - 1)] = '\0';
350
351 strncpy(p_section->stitle, stitle, sizeof(p_section->stitle - 1));
352 p_section->stitle[sizeof(p_section->stitle - 1)] = '\0';
353
354 strncpy(p_section->master_name, master_name, sizeof(p_section->master_name - 1));
355 p_section->master_name[sizeof(p_section->master_name - 1)] = '\0';
356
357 if (trie_dict_set(p_trie_dict_section_list, sname, section_list_count) != 1)
358 {
359 log_error("trie_dict_set(section_data, %s, %d) error\n", sname, section_list_count);
360 return NULL;
361 }
362
363 section_list_reset_articles(p_section);
364
365 section_list_count++;
366
367 return p_section;
368 }
369
370 void section_list_reset_articles(SECTION_LIST *p_section)
371 {
372 p_section->article_count = 0;
373 p_section->topic_count = 0;
374 p_section->visible_article_count = 0;
375 p_section->visible_topic_count = 0;
376 p_section->p_article_head = NULL;
377 p_section->p_article_tail = NULL;
378
379 p_section->page_count = 0;
380 p_section->last_page_visible_article_count = 0;
381 }
382
383 void section_list_cleanup(void)
384 {
385 if (p_trie_dict_section_list != NULL)
386 {
387 trie_dict_destroy(p_trie_dict_section_list);
388 p_trie_dict_section_list = NULL;
389 }
390
391 if (p_section_list_pool != NULL)
392 {
393 free(p_section_list_pool);
394 p_section_list_pool = NULL;
395 }
396
397 section_list_count = 0;
398 }
399
400 SECTION_LIST *section_list_find_by_name(const char *sname)
401 {
402 int64_t index;
403
404 if (p_section_list_pool == NULL || p_trie_dict_section_list == NULL)
405 {
406 log_error("section_list not initialized\n");
407 return NULL;
408 }
409
410 if (trie_dict_get(p_trie_dict_section_list, sname, &index) != 1)
411 {
412 log_error("trie_dict_get(section_data, %s) error\n", sname);
413 return NULL;
414 }
415
416 return (p_section_list_pool + index);
417 }
418
419 int section_list_append_article(SECTION_LIST *p_section, const ARTICLE *p_article_src)
420 {
421 ARTICLE_BLOCK *p_block;
422 int32_t last_aid = 0;
423 ARTICLE *p_article;
424 ARTICLE *p_topic_head;
425 ARTICLE *p_topic_tail;
426
427 if (p_section == NULL || p_article_src == NULL)
428 {
429 log_error("section_list_append_article() NULL pointer error\n");
430 return -1;
431 }
432
433 if (p_article_block_pool == NULL)
434 {
435 log_error("article_block_pool not initialized\n");
436 return -1;
437 }
438
439 if (p_section->article_count >= BBS_article_limit_per_section)
440 {
441 log_error("section_list_append_article() error: article_count reach limit in section %d\n", p_section->sid);
442 return -2;
443 }
444
445 if (p_article_block_pool->block_count == 0 ||
446 p_article_block_pool->p_block[p_article_block_pool->block_count - 1]->article_count >= ARTICLE_PER_BLOCK)
447 {
448 if ((p_block = pop_free_article_block()) == NULL)
449 {
450 log_error("pop_free_article_block() error\n");
451 return -2;
452 }
453
454 if (p_article_block_pool->block_count > 0)
455 {
456 last_aid = p_article_block_pool->p_block[p_article_block_pool->block_count - 1]->articles[ARTICLE_PER_BLOCK - 1].aid;
457 }
458
459 p_article_block_pool->p_block[p_article_block_pool->block_count] = p_block;
460 p_article_block_pool->block_count++;
461 }
462 else
463 {
464 p_block = p_article_block_pool->p_block[p_article_block_pool->block_count - 1];
465 last_aid = p_block->articles[p_block->article_count - 1].aid;
466 }
467
468 // AID of articles should be strictly ascending
469 if (p_article_src->aid <= last_aid)
470 {
471 log_error("section_data_append_article(aid=%d) error: last_aid=%d\n", p_article_src->aid, last_aid);
472 return -3;
473 }
474
475 p_article = (p_block->articles + p_block->article_count);
476 p_block->article_count++;
477 p_section->article_count++;
478
479 // Copy article data
480 *p_article = *p_article_src;
481
482 if (p_article->visible)
483 {
484 p_section->visible_article_count++;
485 }
486
487 // Link appended article as tail node of topic bi-directional list
488 if (p_article->tid != 0)
489 {
490 p_topic_head = article_block_find_by_aid(p_article->tid);
491 if (p_topic_head == NULL)
492 {
493 log_error("search head of topic (aid=%d) error\n", p_article->tid);
494 return -4;
495 }
496
497 p_topic_tail = p_topic_head->p_topic_prior;
498 if (p_topic_tail == NULL)
499 {
500 log_error("tail of topic (aid=%d) is NULL\n", p_article->tid);
501 return -4;
502 }
503 }
504 else
505 {
506 p_section->topic_count++;
507
508 if (p_article->visible)
509 {
510 p_section->visible_topic_count++;
511 }
512
513 p_topic_head = p_article;
514 p_topic_tail = p_article;
515 }
516
517 p_article->p_topic_prior = p_topic_tail;
518 p_article->p_topic_next = p_topic_head;
519 p_topic_head->p_topic_prior = p_article;
520 p_topic_tail->p_topic_next = p_article;
521
522 // Link appended article as tail node of article bi-directional list
523 if (p_section->p_article_head == NULL)
524 {
525 p_section->p_article_head = p_article;
526 p_section->p_article_tail = p_article;
527 }
528 p_article->p_prior = p_section->p_article_tail;
529 p_article->p_next = p_section->p_article_head;
530 p_section->p_article_head->p_prior = p_article;
531 p_section->p_article_tail->p_next = p_article;
532 p_section->p_article_tail = p_article;
533
534 // Update page
535 if ((p_article->visible && p_section->last_page_visible_article_count % BBS_article_limit_per_page == 0) ||
536 p_section->article_count == 1)
537 {
538 p_section->p_page_first_article[p_section->page_count] = p_article;
539 p_section->page_count++;
540 p_section->last_page_visible_article_count = 0;
541 }
542
543 if (p_article->visible)
544 {
545 p_section->last_page_visible_article_count++;
546 }
547
548 return 0;
549 }
550
551 int section_list_set_article_visible(SECTION_LIST *p_section, int32_t aid, int8_t visible)
552 {
553 ARTICLE *p_article;
554 ARTICLE *p_reply;
555 int affected_count = 0;
556
557 if (p_section == NULL)
558 {
559 log_error("section_list_set_article_visible() NULL pointer error\n");
560 return -2;
561 }
562
563 p_article = article_block_find_by_aid(aid);
564 if (p_article == NULL)
565 {
566 return -1; // Not found
567 }
568
569 if (p_article->visible == visible)
570 {
571 return 0; // Already set
572 }
573
574 if (visible == 0) // 1 -> 0
575 {
576 p_section->visible_article_count--;
577
578 if (p_article->tid == 0)
579 {
580 p_section->visible_topic_count--;
581
582 // Set related visible replies to invisible
583 for (p_reply = p_article->p_topic_next; p_reply->tid != 0; p_reply = p_reply->p_topic_next)
584 {
585 if (p_reply->tid != aid)
586 {
587 log_error("Inconsistent tid = %d found in reply %d of topic %d\n", p_reply->tid, p_reply->aid, aid);
588 continue;
589 }
590
591 if (p_reply->visible == 1)
592 {
593 p_reply->visible = 0;
594 p_section->visible_article_count--;
595 affected_count++;
596 }
597 }
598 }
599 }
600 else // 0 -> 1
601 {
602 p_section->visible_article_count++;
603
604 if (p_article->tid == 0)
605 {
606 p_section->visible_topic_count++;
607 }
608 }
609
610 p_article->visible = visible;
611 affected_count++;
612
613 return affected_count;
614 }
615
616 ARTICLE *section_list_find_article_with_offset(SECTION_LIST *p_section, int32_t aid, int32_t *p_page, int32_t *p_offset, ARTICLE **pp_next)
617 {
618 ARTICLE *p_article;
619 int left;
620 int right;
621 int mid;
622
623 *p_page = -1;
624 *p_offset = -1;
625 *pp_next = NULL;
626
627 if (p_section == NULL)
628 {
629 log_error("section_list_find_article_with_offset() NULL pointer error\n");
630 return NULL;
631 }
632
633 if (p_section->article_count == 0) // empty
634 {
635 *p_page = 0;
636 *p_offset = 0;
637 return NULL;
638 }
639
640 left = 0;
641 right = p_section->page_count;
642
643 // aid in the range [ head aid of pages[left], tail aid of pages[right - 1] ]
644 while (left < right - 1)
645 {
646 // get page id no less than mid value of left page id and right page id
647 mid = (left + right) / 2 + (right - left) % 2;
648
649 if (mid >= p_section->page_count)
650 {
651 log_error("page id (mid = %d) is out of boundary\n", mid);
652 return NULL;
653 }
654
655 if (aid < p_section->p_page_first_article[mid]->aid)
656 {
657 right = mid;
658 }
659 else
660 {
661 left = mid;
662 }
663 }
664
665 *p_page = left;
666
667 p_article = p_section->p_page_first_article[*p_page];
668
669 // p_section->p_page_first_article[*p_page]->aid <= aid < p_section->p_page_first_article[*p_page + 1]->aid
670 right = (*p_page == MAX(0, p_section->page_count - 1) ? INT32_MAX : p_section->p_page_first_article[*p_page + 1]->aid);
671
672 // left will be the offset of article found or offset to insert
673 left = 0;
674
675 while (aid > p_article->aid)
676 {
677 p_article = p_article->p_next;
678 left++;
679
680 if (aid == p_article->aid)
681 {
682 *pp_next = p_article->p_next;
683 break;
684 }
685
686 // over last article in the page
687 if (p_article == p_section->p_article_head || p_article->aid >= right)
688 {
689 *pp_next = (p_article == p_section->p_article_head ? p_section->p_article_head : p_section->p_page_first_article[*p_page + 1]);
690 *p_offset = left;
691 return NULL; // not found
692 }
693 }
694
695 if (aid < p_article->aid)
696 {
697 *pp_next = p_article;
698 p_article = NULL; // not found
699 }
700 else // aid == p_article->aid
701 {
702 *pp_next = p_article->p_next;
703 }
704
705 *p_offset = left;
706
707 return p_article;
708 }
709
710 int section_list_calculate_page(SECTION_LIST *p_section, int32_t start_aid)
711 {
712 ARTICLE *p_article;
713 ARTICLE *p_next;
714 int32_t page;
715 int32_t offset;
716 int visible_article_count;
717 int page_head_set;
718
719 if (p_section == NULL)
720 {
721 log_error("section_list_calculate_page() NULL pointer error\n");
722 return -1;
723 }
724
725 if (p_section->article_count == 0) // empty
726 {
727 p_section->page_count = 0;
728 p_section->last_page_visible_article_count = 0;
729
730 return 0;
731 }
732
733 if (start_aid > 0)
734 {
735 p_article = section_list_find_article_with_offset(p_section, start_aid, &page, &offset, &p_next);
736 if (p_article == NULL)
737 {
738 if (page < 0)
739 {
740 return -1;
741 }
742 log_error("section_list_calculate_page() aid = %d not found in section sid = %d\n",
743 start_aid, p_section->sid);
744 return -2;
745 }
746
747 if (offset > 0)
748 {
749 p_article = p_section->p_page_first_article[page];
750 }
751 }
752 else
753 {
754 p_article = p_section->p_article_head;
755 page = 0;
756 offset = 0;
757 }
758
759 visible_article_count = 0;
760 page_head_set = 0;
761
762 do
763 {
764 if (!page_head_set && visible_article_count == 0)
765 {
766 p_section->p_page_first_article[page] = p_article;
767 page_head_set = 1;
768 }
769
770 if (p_article->visible)
771 {
772 visible_article_count++;
773 }
774
775 p_article = p_article->p_next;
776
777 // skip remaining invisible articles
778 while (p_article->visible == 0 && p_article != p_section->p_article_head)
779 {
780 p_article = p_article->p_next;
781 }
782
783 if (visible_article_count >= BBS_article_limit_per_page && p_article != p_section->p_article_head)
784 {
785 page++;
786 visible_article_count = 0;
787 page_head_set = 0;
788
789 if (page >= BBS_article_limit_per_section / BBS_article_limit_per_page && p_article != p_section->p_article_head)
790 {
791 log_error("Count of page exceed limit in section %d\n", p_section->sid);
792 break;
793 }
794 }
795 } while (p_article != p_section->p_article_head);
796
797 p_section->page_count = page + (visible_article_count > 0 ? 1 : 0);
798 p_section->last_page_visible_article_count = visible_article_count;
799
800 return 0;
801 }
802
803 int section_list_count_of_topic_articles(int32_t aid)
804 {
805 ARTICLE *p_article;
806 int article_count;
807
808 p_article = article_block_find_by_aid(aid);
809 if (p_article == NULL)
810 {
811 return 0; // Not found
812 }
813
814 article_count = 0;
815
816 do
817 {
818 article_count++;
819 p_article = p_article->p_topic_next;
820 } while (p_article->aid != aid);
821
822 return article_count;
823 }
824
825 int section_list_move_topic(SECTION_LIST *p_section_src, SECTION_LIST *p_section_dest, int32_t aid)
826 {
827 ARTICLE *p_article;
828 ARTICLE *p_next;
829 int32_t page;
830 int32_t offset;
831 int32_t move_article_count;
832 int32_t dest_article_count_old;
833 int32_t last_unaffected_aid_src;
834
835 if (p_section_dest == NULL)
836 {
837 log_error("section_list_move_topic() NULL pointer error\n");
838 return -1;
839 }
840
841 if ((p_article = section_list_find_article_with_offset(p_section_src, aid, &page, &offset, &p_next)) == NULL)
842 {
843 log_error("section_list_move_topic() error: article %d not found in section %d\n", aid, p_section_src->sid);
844 return -2;
845 }
846
847 if (p_article->tid != 0)
848 {
849 log_error("section_list_move_topic(aid = %d) error: article is not head of topic, tid = %d\n", aid, p_article->tid);
850 return -2;
851 }
852
853 last_unaffected_aid_src = (p_article == p_section_src->p_article_head ? 0 : p_article->p_prior->aid);
854
855 move_article_count = section_list_count_of_topic_articles(aid);
856 if (move_article_count <= 0)
857 {
858 log_error("section_list_count_of_topic_articles(aid = %d) <= 0\n", aid);
859 return -2;
860 }
861
862 if (p_section_dest->article_count + move_article_count > BBS_article_limit_per_section)
863 {
864 log_error("section_list_move_topic() error: article_count %d reach limit in section %d\n",
865 p_section_dest->article_count + move_article_count, p_section_dest->sid);
866 return -3;
867 }
868
869 dest_article_count_old = p_section_dest->article_count;
870
871 do
872 {
873 if (section_list_find_article_with_offset(p_section_dest, p_article->aid, &page, &offset, &p_next) != NULL)
874 {
875 log_error("section_list_move_topic() error: article %d already in section %d\n", p_article->aid, p_section_dest->sid);
876 return -4;
877 }
878
879 // Remove from bi-directional article list of src section
880 if (p_section_src->p_article_head == p_article)
881 {
882 p_section_src->p_article_head = p_article->p_next;
883 }
884 if (p_section_src->p_article_tail == p_article)
885 {
886 p_section_src->p_article_tail = p_article->p_prior;
887 }
888 if (p_section_src->p_article_head == p_article) // || p_section_src->p_article_tail == p_article
889 {
890 p_section_src->p_article_head = NULL;
891 p_section_src->p_article_tail = NULL;
892 }
893
894 p_article->p_prior->p_next = p_article->p_next;
895 p_article->p_next->p_prior = p_article->p_prior;
896
897 // Insert into bi-directional article list of dest section
898 if (p_next == NULL) // empty section
899 {
900 p_section_dest->p_article_head = p_article;
901 p_section_dest->p_article_tail = p_article;
902 p_article->p_prior = p_article;
903 p_article->p_next = p_article;
904 }
905 else
906 {
907 if (p_section_dest->p_article_head == p_next)
908 {
909 if (p_article->aid < p_next->aid)
910 {
911 p_section_dest->p_article_head = p_article;
912 }
913 else // p_article->aid > p_next->aid
914 {
915 p_section_dest->p_article_tail = p_article;
916 }
917 }
918
919 p_article->p_prior = p_next->p_prior;
920 p_article->p_next = p_next;
921 p_next->p_prior->p_next = p_article;
922 p_next->p_prior = p_article;
923 }
924
925 // Update article / topic counter of src / desc section
926 p_section_src->article_count--;
927 p_section_dest->article_count++;
928 if (p_article->tid == 0)
929 {
930 p_section_src->topic_count--;
931 p_section_dest->topic_count++;
932 }
933
934 // Update visible article / topic counter of src / desc section
935 if (p_article->visible)
936 {
937 p_section_src->visible_article_count--;
938 p_section_dest->visible_article_count++;
939 if (p_article->tid == 0)
940 {
941 p_section_src->visible_topic_count--;
942 p_section_dest->visible_topic_count++;
943 }
944 }
945
946 // Update page for empty dest section
947 if (p_section_dest->article_count == 1)
948 {
949 p_section_dest->p_page_first_article[0] = p_article;
950 p_section_dest->page_count = 1;
951 p_section_dest->last_page_visible_article_count = (p_article->visible ? 1 : 0);
952 }
953
954 p_article = p_article->p_topic_next;
955 } while (p_article->aid != aid);
956
957 if (p_section_dest->article_count - dest_article_count_old != move_article_count)
958 {
959 log_error("section_list_move_topic() error: count of moved articles %d != %d\n",
960 p_section_dest->article_count - dest_article_count_old, move_article_count);
961 }
962
963 // Re-calculate pages of both src and desc sections
964 if (section_list_calculate_page(p_section_src, last_unaffected_aid_src) < 0)
965 {
966 log_error("section_list_calculate_page(section = %d, aid = %d) error at aid = %d\n", p_section_src->sid, last_unaffected_aid_src, aid);
967 }
968 if (section_list_calculate_page(p_section_dest, aid) < 0)
969 {
970 log_error("section_list_calculate_page(section = %d, aid = %d) error\n", p_section_dest->sid, aid);
971 }
972
973 return move_article_count;
974 }

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