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

Annotation of /lbbs/src/section_list.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.58 - (hide annotations)
Mon Nov 17 11:56:11 2025 UTC (3 months, 4 weeks ago) by sysadm
Branch: MAIN
Changes since 1.57: +16 -4 lines
Content type: text/x-csrc
Add implementation for MSYS2

1 sysadm 1.51 /* SPDX-License-Identifier: GPL-3.0-or-later */
2     /*
3     * section_list
4     * - data models and basic operations of section and article
5     *
6 sysadm 1.53 * Copyright (C) 2004-2025 Leaflet <leaflet@leafok.com>
7 sysadm 1.51 */
8 sysadm 1.1
9 sysadm 1.56 #ifdef HAVE_CONFIG_H
10     #include "config.h"
11     #endif
12    
13 sysadm 1.34 #include "log.h"
14 sysadm 1.1 #include "section_list.h"
15     #include "trie_dict.h"
16 sysadm 1.46 #include "user_list.h"
17 sysadm 1.34 #include <errno.h>
18     #include <signal.h>
19 sysadm 1.1 #include <stdio.h>
20 sysadm 1.34 #include <stdlib.h>
21 sysadm 1.1 #include <string.h>
22     #include <unistd.h>
23 sysadm 1.34 #include <sys/ipc.h>
24 sysadm 1.7 #include <sys/param.h>
25 sysadm 1.16 #include <sys/sem.h>
26 sysadm 1.1 #include <sys/shm.h>
27    
28 sysadm 1.58 #if defined(_SEM_SEMUN_UNDEFINED) || defined(__MSYS__) || defined(__MINGW32__)
29 sysadm 1.17 union semun
30     {
31     int val; /* Value for SETVAL */
32     struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
33     unsigned short *array; /* Array for GETALL, SETALL */
34     struct seminfo *__buf; /* Buffer for IPC_INFO
35     (Linux-specific) */
36     };
37 sysadm 1.58 #endif // #if defined(_SEM_SEMUN_UNDEFINED)
38 sysadm 1.17
39 sysadm 1.55 enum _section_list_constant_t
40     {
41     SECTION_TRY_LOCK_WAIT_TIME = 1, // second
42     SECTION_TRY_LOCK_TIMES = 10,
43 sysadm 1.23
44 sysadm 1.55 ARTICLE_BLOCK_PER_SHM = 1000, // sizeof(ARTICLE_BLOCK) * ARTICLE_BLOCK_PER_SHM is the size of each shm segment to allocate
45     ARTICLE_BLOCK_SHM_COUNT_LIMIT = 80, // limited by length (8-bit) of proj_id in ftok(path, proj_id)
46     ARTICLE_BLOCK_PER_POOL = (ARTICLE_BLOCK_PER_SHM * ARTICLE_BLOCK_SHM_COUNT_LIMIT),
47 sysadm 1.7
48 sysadm 1.55 CALCULATE_PAGE_THRESHOLD = 100, // Adjust to tune performance of moving topic between sections
49 sysadm 1.12
50 sysadm 1.55 SID_STR_LEN = 5, // 32-bit + NULL
51     };
52 sysadm 1.14
53 sysadm 1.7 struct article_block_t
54     {
55 sysadm 1.54 ARTICLE articles[BBS_article_count_per_block];
56 sysadm 1.22 int article_count;
57 sysadm 1.7 struct article_block_t *p_next_block;
58     };
59     typedef struct article_block_t ARTICLE_BLOCK;
60 sysadm 1.1
61 sysadm 1.22 struct article_block_pool_t
62 sysadm 1.1 {
63     int shmid;
64 sysadm 1.22 struct
65     {
66     int shmid;
67     void *p_shm;
68     } shm_pool[ARTICLE_BLOCK_SHM_COUNT_LIMIT];
69 sysadm 1.7 int shm_count;
70     ARTICLE_BLOCK *p_block_free_list;
71     ARTICLE_BLOCK *p_block[ARTICLE_BLOCK_PER_POOL];
72 sysadm 1.22 int block_count;
73 sysadm 1.7 };
74     typedef struct article_block_pool_t ARTICLE_BLOCK_POOL;
75    
76     static ARTICLE_BLOCK_POOL *p_article_block_pool = NULL;
77 sysadm 1.1
78 sysadm 1.38 SECTION_LIST_POOL *p_section_list_pool = NULL;
79 sysadm 1.7
80     int article_block_init(const char *filename, int block_count)
81 sysadm 1.1 {
82     int shmid;
83     int proj_id;
84     key_t key;
85     size_t size;
86     void *p_shm;
87     int i;
88 sysadm 1.7 int block_count_in_shm;
89     ARTICLE_BLOCK *p_block_in_shm;
90     ARTICLE_BLOCK **pp_block_next;
91    
92     if (p_article_block_pool != NULL)
93 sysadm 1.1 {
94 sysadm 1.7 log_error("article_block_pool already initialized\n");
95 sysadm 1.1 return -1;
96     }
97    
98 sysadm 1.27 if (block_count <= 0 || block_count > ARTICLE_BLOCK_PER_POOL)
99 sysadm 1.1 {
100 sysadm 1.7 log_error("article_block_count exceed limit %d\n", ARTICLE_BLOCK_PER_POOL);
101 sysadm 1.1 return -2;
102     }
103    
104 sysadm 1.18 // Allocate shared memory
105     proj_id = ARTICLE_BLOCK_SHM_COUNT_LIMIT; // keep different from proj_id used to create block shm
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     return -3;
111     }
112    
113     size = sizeof(ARTICLE_BLOCK_POOL);
114     shmid = shmget(key, size, IPC_CREAT | IPC_EXCL | 0600);
115     if (shmid == -1)
116     {
117     log_error("shmget(article_block_pool_shm, size = %d) error (%d)\n", size, errno);
118     return -3;
119     }
120     p_shm = shmat(shmid, NULL, 0);
121     if (p_shm == (void *)-1)
122 sysadm 1.1 {
123 sysadm 1.18 log_error("shmat(shmid = %d) error (%d)\n", shmid, errno);
124     return -3;
125 sysadm 1.1 }
126    
127 sysadm 1.18 p_article_block_pool = p_shm;
128 sysadm 1.22 p_article_block_pool->shmid = shmid;
129 sysadm 1.18
130 sysadm 1.7 p_article_block_pool->shm_count = 0;
131     pp_block_next = &(p_article_block_pool->p_block_free_list);
132 sysadm 1.1
133 sysadm 1.7 while (block_count > 0)
134 sysadm 1.1 {
135 sysadm 1.7 block_count_in_shm = MIN(block_count, ARTICLE_BLOCK_PER_SHM);
136     block_count -= block_count_in_shm;
137 sysadm 1.1
138 sysadm 1.18 proj_id = p_article_block_pool->shm_count;
139 sysadm 1.1 key = ftok(filename, proj_id);
140     if (key == -1)
141     {
142     log_error("ftok(%s, %d) error (%d)\n", filename, proj_id, errno);
143 sysadm 1.7 article_block_cleanup();
144 sysadm 1.1 return -3;
145     }
146    
147 sysadm 1.19 size = sizeof(ARTICLE_BLOCK) * (size_t)block_count_in_shm;
148 sysadm 1.1 shmid = shmget(key, size, IPC_CREAT | IPC_EXCL | 0600);
149     if (shmid == -1)
150     {
151 sysadm 1.7 log_error("shmget(shm_index = %d, size = %d) error (%d)\n", p_article_block_pool->shm_count, size, errno);
152     article_block_cleanup();
153 sysadm 1.1 return -3;
154     }
155     p_shm = shmat(shmid, NULL, 0);
156     if (p_shm == (void *)-1)
157     {
158     log_error("shmat(shmid = %d) error (%d)\n", shmid, errno);
159 sysadm 1.7 article_block_cleanup();
160 sysadm 1.1 return -3;
161     }
162    
163 sysadm 1.7 (p_article_block_pool->shm_pool + p_article_block_pool->shm_count)->shmid = shmid;
164     (p_article_block_pool->shm_pool + p_article_block_pool->shm_count)->p_shm = p_shm;
165     p_article_block_pool->shm_count++;
166 sysadm 1.1
167 sysadm 1.7 p_block_in_shm = p_shm;
168     *pp_block_next = p_block_in_shm;
169 sysadm 1.1
170 sysadm 1.7 for (i = 0; i < block_count_in_shm; i++)
171 sysadm 1.1 {
172 sysadm 1.7 if (i < block_count_in_shm - 1)
173 sysadm 1.1 {
174 sysadm 1.7 (p_block_in_shm + i)->p_next_block = (p_block_in_shm + i + 1);
175 sysadm 1.1 }
176     else
177     {
178 sysadm 1.7 (p_block_in_shm + i)->p_next_block = NULL;
179     pp_block_next = &((p_block_in_shm + i)->p_next_block);
180 sysadm 1.1 }
181     }
182     }
183    
184 sysadm 1.7 p_article_block_pool->block_count = 0;
185    
186 sysadm 1.1 return 0;
187     }
188    
189 sysadm 1.7 void article_block_cleanup(void)
190     {
191 sysadm 1.22 int shmid;
192    
193 sysadm 1.18 if (p_article_block_pool == NULL)
194     {
195     return;
196     }
197    
198     for (int i = 0; i < p_article_block_pool->shm_count; i++)
199 sysadm 1.7 {
200 sysadm 1.18 if (shmdt((p_article_block_pool->shm_pool + i)->p_shm) == -1)
201 sysadm 1.7 {
202 sysadm 1.18 log_error("shmdt(shmid = %d) error (%d)\n", (p_article_block_pool->shm_pool + i)->shmid, errno);
203     }
204 sysadm 1.7
205 sysadm 1.18 if (shmctl((p_article_block_pool->shm_pool + i)->shmid, IPC_RMID, NULL) == -1)
206     {
207     log_error("shmctl(shmid = %d, IPC_RMID) error (%d)\n", (p_article_block_pool->shm_pool + i)->shmid, errno);
208 sysadm 1.7 }
209 sysadm 1.18 }
210    
211 sysadm 1.22 shmid = p_article_block_pool->shmid;
212    
213 sysadm 1.18 if (shmdt(p_article_block_pool) == -1)
214     {
215 sysadm 1.22 log_error("shmdt(shmid = %d) error (%d)\n", shmid, errno);
216 sysadm 1.18 }
217 sysadm 1.7
218 sysadm 1.52 if (shmid != 0 && shmctl(shmid, IPC_RMID, NULL) == -1)
219 sysadm 1.18 {
220 sysadm 1.22 log_error("shmctl(shmid = %d, IPC_RMID) error (%d)\n", shmid, errno);
221 sysadm 1.7 }
222 sysadm 1.18
223     p_article_block_pool = NULL;
224 sysadm 1.7 }
225    
226 sysadm 1.26 int set_article_block_shm_readonly(void)
227     {
228     int shmid;
229     void *p_shm;
230     int i;
231    
232     if (p_article_block_pool == NULL)
233     {
234     log_error("article_block_pool not initialized\n");
235     return -1;
236     }
237    
238     for (i = 0; i < p_article_block_pool->shm_count; i++)
239     {
240     shmid = (p_article_block_pool->shm_pool + i)->shmid;
241    
242     // Remap shared memory in read-only mode
243     p_shm = shmat(shmid, (p_article_block_pool->shm_pool + i)->p_shm, SHM_RDONLY | SHM_REMAP);
244     if (p_shm == (void *)-1)
245     {
246     log_error("shmat(article_block_pool shmid = %d) error (%d)\n", shmid, errno);
247     return -2;
248     }
249     }
250    
251     return 0;
252     }
253    
254     int detach_article_block_shm(void)
255     {
256     int shmid;
257    
258     if (p_article_block_pool == NULL)
259     {
260     return -1;
261     }
262    
263     for (int i = 0; i < p_article_block_pool->shm_count; i++)
264     {
265     if ((p_article_block_pool->shm_pool + i)->p_shm != NULL && shmdt((p_article_block_pool->shm_pool + i)->p_shm) == -1)
266     {
267     log_error("shmdt(shmid = %d) error (%d)\n", (p_article_block_pool->shm_pool + i)->shmid, errno);
268     return -2;
269     }
270     }
271    
272     shmid = p_article_block_pool->shmid;
273    
274     if (shmdt(p_article_block_pool) == -1)
275     {
276     log_error("shmdt(shmid = %d) error (%d)\n", shmid, errno);
277     return -3;
278     }
279    
280     p_article_block_pool = NULL;
281    
282     return 0;
283     }
284    
285 sysadm 1.7 inline static ARTICLE_BLOCK *pop_free_article_block(void)
286     {
287     ARTICLE_BLOCK *p_block = NULL;
288    
289     if (p_article_block_pool->p_block_free_list != NULL)
290     {
291     p_block = p_article_block_pool->p_block_free_list;
292     p_article_block_pool->p_block_free_list = p_block->p_next_block;
293     p_block->p_next_block = NULL;
294     p_block->article_count = 0;
295     }
296    
297     return p_block;
298     }
299    
300     inline static void push_free_article_block(ARTICLE_BLOCK *p_block)
301 sysadm 1.1 {
302 sysadm 1.7 p_block->p_next_block = p_article_block_pool->p_block_free_list;
303     p_article_block_pool->p_block_free_list = p_block;
304     }
305    
306     int article_block_reset(void)
307     {
308     ARTICLE_BLOCK *p_block;
309    
310     if (p_article_block_pool == NULL)
311     {
312     log_error("article_block_pool not initialized\n");
313     return -1;
314     }
315 sysadm 1.1
316 sysadm 1.7 while (p_article_block_pool->block_count > 0)
317 sysadm 1.1 {
318 sysadm 1.7 p_article_block_pool->block_count--;
319     p_block = p_article_block_pool->p_block[p_article_block_pool->block_count];
320     push_free_article_block(p_block);
321 sysadm 1.1 }
322    
323 sysadm 1.7 return 0;
324     }
325    
326     ARTICLE *article_block_find_by_aid(int32_t aid)
327     {
328     ARTICLE_BLOCK *p_block;
329     int left;
330     int right;
331     int mid;
332    
333     if (p_article_block_pool == NULL)
334 sysadm 1.1 {
335 sysadm 1.7 log_error("article_block_pool not initialized\n");
336     return NULL;
337 sysadm 1.1 }
338    
339 sysadm 1.7 if (p_article_block_pool->block_count == 0) // empty
340 sysadm 1.1 {
341 sysadm 1.7 return NULL;
342 sysadm 1.1 }
343    
344 sysadm 1.7 left = 0;
345 sysadm 1.47 right = p_article_block_pool->block_count - 1;
346 sysadm 1.7
347 sysadm 1.47 // aid in the range [ head aid of blocks[left], tail aid of blocks[right] ]
348     while (left < right)
349 sysadm 1.1 {
350 sysadm 1.7 // get block offset no less than mid value of left and right block offsets
351 sysadm 1.47 mid = (left + right) / 2 + (left + right) % 2;
352 sysadm 1.1
353 sysadm 1.7 if (aid < p_article_block_pool->p_block[mid]->articles[0].aid)
354     {
355 sysadm 1.47 right = mid - 1;
356 sysadm 1.7 }
357 sysadm 1.47 else // if (aid >= p_article_block_pool->p_block[mid]->articles[0].aid)
358 sysadm 1.7 {
359     left = mid;
360 sysadm 1.1 }
361 sysadm 1.7 }
362    
363     p_block = p_article_block_pool->p_block[left];
364    
365     left = 0;
366     right = p_block->article_count - 1;
367    
368     // aid in the range [ aid of articles[left], aid of articles[right] ]
369     while (left < right)
370     {
371     mid = (left + right) / 2;
372 sysadm 1.1
373 sysadm 1.7 if (aid <= p_block->articles[mid].aid)
374     {
375     right = mid;
376     }
377     else
378     {
379     left = mid + 1;
380     }
381 sysadm 1.1 }
382 sysadm 1.7
383 sysadm 1.28 if (aid != p_block->articles[left].aid) // not found
384     {
385     return NULL;
386     }
387    
388     return (p_block->articles + left); // found
389 sysadm 1.1 }
390    
391 sysadm 1.7 ARTICLE *article_block_find_by_index(int index)
392 sysadm 1.1 {
393 sysadm 1.7 ARTICLE_BLOCK *p_block;
394    
395     if (p_article_block_pool == NULL)
396     {
397     log_error("article_block_pool not initialized\n");
398     return NULL;
399     }
400    
401 sysadm 1.54 if (index < 0 || index / BBS_article_count_per_block >= p_article_block_pool->block_count)
402 sysadm 1.7 {
403 sysadm 1.14 log_error("article_block_find_by_index(%d) is out of boundary of block [0, %d)\n", index, p_article_block_pool->block_count);
404 sysadm 1.7 return NULL;
405     }
406    
407 sysadm 1.54 p_block = p_article_block_pool->p_block[index / BBS_article_count_per_block];
408 sysadm 1.1
409 sysadm 1.54 if (index % BBS_article_count_per_block >= p_block->article_count)
410 sysadm 1.1 {
411 sysadm 1.14 log_error("article_block_find_by_index(%d) is out of boundary of article [0, %d)\n", index, p_block->article_count);
412 sysadm 1.7 return NULL;
413 sysadm 1.1 }
414    
415 sysadm 1.54 return (p_block->articles + (index % BBS_article_count_per_block));
416 sysadm 1.1 }
417    
418 sysadm 1.19 extern int section_list_init(const char *filename)
419 sysadm 1.1 {
420 sysadm 1.16 int semid;
421 sysadm 1.13 int shmid;
422     int proj_id;
423     key_t key;
424     size_t size;
425     void *p_shm;
426 sysadm 1.17 union semun arg;
427     int i;
428 sysadm 1.13
429 sysadm 1.22 if (p_section_list_pool != NULL)
430 sysadm 1.13 {
431 sysadm 1.19 section_list_cleanup();
432 sysadm 1.13 }
433 sysadm 1.7
434 sysadm 1.13 proj_id = (int)(time(NULL) % getpid());
435     key = ftok(filename, proj_id);
436     if (key == -1)
437     {
438     log_error("ftok(%s, %d) error (%d)\n", filename, proj_id, errno);
439     return -3;
440     }
441 sysadm 1.1
442 sysadm 1.22 // Allocate shared memory
443     size = sizeof(SECTION_LIST_POOL);
444     shmid = shmget(key, size, IPC_CREAT | IPC_EXCL | 0600);
445     if (shmid == -1)
446     {
447     log_error("shmget(section_list_pool_shm, size = %d) error (%d)\n", size, errno);
448     return -3;
449     }
450     p_shm = shmat(shmid, NULL, 0);
451     if (p_shm == (void *)-1)
452     {
453     log_error("shmat(shmid = %d) error (%d)\n", shmid, errno);
454     return -3;
455     }
456    
457     p_section_list_pool = p_shm;
458     p_section_list_pool->shmid = shmid;
459     p_section_list_pool->section_count = 0;
460    
461     // Allocate semaphore as section locks
462 sysadm 1.16 size = 2 * (BBS_max_section + 1); // r_sem and w_sem per section, the last pair for all sections
463     semid = semget(key, (int)size, IPC_CREAT | IPC_EXCL | 0600);
464     if (semid == -1)
465     {
466 sysadm 1.17 log_error("semget(section_list_pool_sem, size = %d) error (%d)\n", size, errno);
467 sysadm 1.16 return -3;
468     }
469    
470 sysadm 1.17 // Initialize sem value to 0
471     arg.val = 0;
472     for (i = 0; i < size; i++)
473     {
474     if (semctl(semid, i, SETVAL, arg) == -1)
475     {
476     log_error("semctl(section_list_pool_sem, SETVAL) error (%d)\n", errno);
477     return -3;
478     }
479     }
480    
481 sysadm 1.22 p_section_list_pool->semid = semid;
482 sysadm 1.16
483 sysadm 1.22 p_section_list_pool->p_trie_dict_section_by_name = trie_dict_create();
484     if (p_section_list_pool->p_trie_dict_section_by_name == NULL)
485 sysadm 1.14 {
486     log_error("trie_dict_create() OOM\n", BBS_max_section);
487     return -2;
488     }
489    
490 sysadm 1.22 p_section_list_pool->p_trie_dict_section_by_sid = trie_dict_create();
491     if (p_section_list_pool->p_trie_dict_section_by_sid == NULL)
492 sysadm 1.1 {
493 sysadm 1.13 log_error("trie_dict_create() OOM\n", BBS_max_section);
494     return -2;
495     }
496    
497     return 0;
498     }
499    
500 sysadm 1.26 void section_list_cleanup(void)
501     {
502     int shmid;
503    
504     if (p_section_list_pool == NULL)
505     {
506     return;
507     }
508    
509     if (p_section_list_pool->p_trie_dict_section_by_name != NULL)
510     {
511     trie_dict_destroy(p_section_list_pool->p_trie_dict_section_by_name);
512     p_section_list_pool->p_trie_dict_section_by_name = NULL;
513     }
514    
515     if (p_section_list_pool->p_trie_dict_section_by_sid != NULL)
516     {
517     trie_dict_destroy(p_section_list_pool->p_trie_dict_section_by_sid);
518     p_section_list_pool->p_trie_dict_section_by_sid = NULL;
519     }
520    
521     shmid = p_section_list_pool->shmid;
522    
523     if (semctl(p_section_list_pool->semid, 0, IPC_RMID) == -1)
524     {
525     log_error("semctl(semid = %d, IPC_RMID) error (%d)\n", p_section_list_pool->semid, errno);
526     }
527    
528     if (shmdt(p_section_list_pool) == -1)
529     {
530     log_error("shmdt(shmid = %d) error (%d)\n", shmid, errno);
531     }
532    
533 sysadm 1.52 if (shmid != 0 && shmctl(shmid, IPC_RMID, NULL) == -1)
534 sysadm 1.26 {
535     log_error("shmctl(shmid = %d, IPC_RMID) error (%d)\n", shmid, errno);
536     }
537    
538     p_section_list_pool = NULL;
539     }
540    
541     int set_section_list_shm_readonly(void)
542     {
543     int shmid;
544     void *p_shm;
545    
546     if (p_section_list_pool == NULL)
547     {
548     log_error("p_section_list_pool not initialized\n");
549     return -1;
550     }
551    
552     shmid = p_section_list_pool->shmid;
553    
554     // Remap shared memory in read-only mode
555     p_shm = shmat(shmid, p_section_list_pool, SHM_RDONLY | SHM_REMAP);
556     if (p_shm == (void *)-1)
557     {
558     log_error("shmat(section_list_pool shmid = %d) error (%d)\n", shmid, errno);
559     return -3;
560     }
561    
562     p_section_list_pool = p_shm;
563    
564     return 0;
565     }
566    
567     int detach_section_list_shm(void)
568     {
569     if (p_section_list_pool != NULL && shmdt(p_section_list_pool) == -1)
570     {
571     log_error("shmdt(section_list_pool) error (%d)\n", errno);
572     return -1;
573     }
574    
575     p_section_list_pool = NULL;
576    
577     return 0;
578     }
579    
580 sysadm 1.14 inline static void sid_to_str(int32_t sid, char *p_sid_str)
581     {
582     uint32_t u_sid;
583     int i;
584    
585     u_sid = (uint32_t)sid;
586     for (i = 0; i < SID_STR_LEN - 1; i++)
587     {
588     p_sid_str[i] = (char)(u_sid % 255 + 1);
589     u_sid /= 255;
590     }
591     p_sid_str[i] = '\0';
592     }
593    
594 sysadm 1.30 SECTION_LIST *section_list_create(int32_t sid, const char *sname, const char *stitle, const char *master_list)
595 sysadm 1.13 {
596     SECTION_LIST *p_section;
597 sysadm 1.14 char sid_str[SID_STR_LEN];
598 sysadm 1.13
599 sysadm 1.22 if (p_section_list_pool == NULL)
600 sysadm 1.13 {
601     log_error("session_list_pool not initialized\n");
602     return NULL;
603 sysadm 1.1 }
604    
605 sysadm 1.22 if (p_section_list_pool->section_count >= BBS_max_section)
606 sysadm 1.1 {
607 sysadm 1.22 log_error("section_count reach limit %d >= %d\n", p_section_list_pool->section_count, BBS_max_section);
608 sysadm 1.1 return NULL;
609     }
610    
611 sysadm 1.14 sid_to_str(sid, sid_str);
612    
613 sysadm 1.22 p_section = p_section_list_pool->sections + p_section_list_pool->section_count;
614 sysadm 1.1
615 sysadm 1.11 p_section->sid = sid;
616 sysadm 1.36 p_section->ex_menu_tm = 0;
617 sysadm 1.11
618 sysadm 1.29 strncpy(p_section->sname, sname, sizeof(p_section->sname) - 1);
619     p_section->sname[sizeof(p_section->sname) - 1] = '\0';
620 sysadm 1.1
621 sysadm 1.29 strncpy(p_section->stitle, stitle, sizeof(p_section->stitle) - 1);
622     p_section->stitle[sizeof(p_section->stitle) - 1] = '\0';
623 sysadm 1.1
624 sysadm 1.30 strncpy(p_section->master_list, master_list, sizeof(p_section->master_list) - 1);
625 sysadm 1.29 p_section->master_list[sizeof(p_section->master_list) - 1] = '\0';
626 sysadm 1.1
627 sysadm 1.22 if (trie_dict_set(p_section_list_pool->p_trie_dict_section_by_name, sname, p_section_list_pool->section_count) != 1)
628 sysadm 1.14 {
629 sysadm 1.22 log_error("trie_dict_set(section, %s, %d) error\n", sname, p_section_list_pool->section_count);
630 sysadm 1.14 return NULL;
631     }
632    
633 sysadm 1.22 if (trie_dict_set(p_section_list_pool->p_trie_dict_section_by_sid, sid_str, p_section_list_pool->section_count) != 1)
634 sysadm 1.1 {
635 sysadm 1.22 log_error("trie_dict_set(section, %d, %d) error\n", sid, p_section_list_pool->section_count);
636 sysadm 1.1 return NULL;
637     }
638    
639 sysadm 1.7 section_list_reset_articles(p_section);
640    
641 sysadm 1.22 p_section_list_pool->section_count++;
642 sysadm 1.1
643     return p_section;
644     }
645    
646 sysadm 1.43 int section_list_update(SECTION_LIST *p_section, const char *sname, const char *stitle, const char *master_list)
647     {
648 sysadm 1.44 int64_t index;
649    
650 sysadm 1.43 if (p_section == NULL || sname == NULL || stitle == NULL || master_list == NULL)
651     {
652     log_error("NULL pointer error\n");
653     return -1;
654     }
655    
656 sysadm 1.44 index = get_section_index(p_section);
657    
658 sysadm 1.43 strncpy(p_section->sname, sname, sizeof(p_section->sname) - 1);
659     p_section->sname[sizeof(p_section->sname) - 1] = '\0';
660    
661     strncpy(p_section->stitle, stitle, sizeof(p_section->stitle) - 1);
662     p_section->stitle[sizeof(p_section->stitle) - 1] = '\0';
663    
664     strncpy(p_section->master_list, master_list, sizeof(p_section->master_list) - 1);
665     p_section->master_list[sizeof(p_section->master_list) - 1] = '\0';
666    
667 sysadm 1.44 if (trie_dict_set(p_section_list_pool->p_trie_dict_section_by_name, sname, index) < 0)
668 sysadm 1.43 {
669 sysadm 1.44 log_error("trie_dict_set(section, %s, %d) error\n", sname, index);
670 sysadm 1.43 return -2;
671     }
672    
673     return 0;
674     }
675    
676 sysadm 1.7 void section_list_reset_articles(SECTION_LIST *p_section)
677 sysadm 1.1 {
678 sysadm 1.7 p_section->article_count = 0;
679 sysadm 1.8 p_section->topic_count = 0;
680     p_section->visible_article_count = 0;
681     p_section->visible_topic_count = 0;
682 sysadm 1.7 p_section->p_article_head = NULL;
683     p_section->p_article_tail = NULL;
684 sysadm 1.1
685 sysadm 1.7 p_section->page_count = 0;
686 sysadm 1.8 p_section->last_page_visible_article_count = 0;
687 sysadm 1.35
688     p_section->ontop_article_count = 0;
689 sysadm 1.7 }
690 sysadm 1.1
691 sysadm 1.41 SECTION_LIST *section_list_find_by_name(const char *sname)
692 sysadm 1.1 {
693     int64_t index;
694 sysadm 1.24 int ret;
695 sysadm 1.1
696 sysadm 1.22 if (p_section_list_pool == NULL)
697 sysadm 1.14 {
698     log_error("section_list not initialized\n");
699     return NULL;
700     }
701    
702 sysadm 1.24 ret = trie_dict_get(p_section_list_pool->p_trie_dict_section_by_name, sname, &index);
703     if (ret < 0)
704 sysadm 1.14 {
705     log_error("trie_dict_get(section, %s) error\n", sname);
706     return NULL;
707     }
708 sysadm 1.24 else if (ret == 0)
709     {
710     return NULL;
711     }
712 sysadm 1.14
713 sysadm 1.22 return (p_section_list_pool->sections + index);
714 sysadm 1.14 }
715    
716 sysadm 1.41 SECTION_LIST *section_list_find_by_sid(int32_t sid)
717 sysadm 1.14 {
718     int64_t index;
719 sysadm 1.24 int ret;
720 sysadm 1.14 char sid_str[SID_STR_LEN];
721    
722 sysadm 1.22 if (p_section_list_pool == NULL)
723 sysadm 1.1 {
724 sysadm 1.7 log_error("section_list not initialized\n");
725 sysadm 1.1 return NULL;
726     }
727    
728 sysadm 1.14 sid_to_str(sid, sid_str);
729    
730 sysadm 1.24 ret = trie_dict_get(p_section_list_pool->p_trie_dict_section_by_sid, sid_str, &index);
731     if (ret < 0)
732 sysadm 1.1 {
733 sysadm 1.14 log_error("trie_dict_get(section, %d) error\n", sid);
734 sysadm 1.1 return NULL;
735     }
736 sysadm 1.24 else if (ret == 0)
737     {
738     return NULL;
739     }
740 sysadm 1.1
741 sysadm 1.22 return (p_section_list_pool->sections + index);
742 sysadm 1.1 }
743    
744 sysadm 1.7 int section_list_append_article(SECTION_LIST *p_section, const ARTICLE *p_article_src)
745 sysadm 1.1 {
746     ARTICLE_BLOCK *p_block;
747     int32_t last_aid = 0;
748 sysadm 1.6 ARTICLE *p_article;
749 sysadm 1.1 ARTICLE *p_topic_head;
750     ARTICLE *p_topic_tail;
751    
752 sysadm 1.6 if (p_section == NULL || p_article_src == NULL)
753 sysadm 1.1 {
754 sysadm 1.42 log_error("NULL pointer error\n");
755 sysadm 1.1 return -1;
756     }
757    
758 sysadm 1.7 if (p_article_block_pool == NULL)
759 sysadm 1.1 {
760 sysadm 1.7 log_error("article_block_pool not initialized\n");
761 sysadm 1.1 return -1;
762     }
763    
764 sysadm 1.14 if (p_section->sid != p_article_src->sid)
765     {
766     log_error("section_list_append_article() error: section sid %d != article sid %d\n", p_section->sid, p_article_src->sid);
767     return -2;
768     }
769    
770 sysadm 1.9 if (p_section->article_count >= BBS_article_limit_per_section)
771     {
772     log_error("section_list_append_article() error: article_count reach limit in section %d\n", p_section->sid);
773     return -2;
774     }
775    
776 sysadm 1.7 if (p_article_block_pool->block_count == 0 ||
777 sysadm 1.54 p_article_block_pool->p_block[p_article_block_pool->block_count - 1]->article_count >= BBS_article_count_per_block)
778 sysadm 1.1 {
779     if ((p_block = pop_free_article_block()) == NULL)
780     {
781     log_error("pop_free_article_block() error\n");
782     return -2;
783     }
784    
785 sysadm 1.7 if (p_article_block_pool->block_count > 0)
786 sysadm 1.1 {
787 sysadm 1.54 last_aid = p_article_block_pool->p_block[p_article_block_pool->block_count - 1]->articles[BBS_article_count_per_block - 1].aid;
788 sysadm 1.1 }
789 sysadm 1.7
790     p_article_block_pool->p_block[p_article_block_pool->block_count] = p_block;
791     p_article_block_pool->block_count++;
792 sysadm 1.1 }
793     else
794     {
795 sysadm 1.7 p_block = p_article_block_pool->p_block[p_article_block_pool->block_count - 1];
796     last_aid = p_block->articles[p_block->article_count - 1].aid;
797 sysadm 1.1 }
798    
799     // AID of articles should be strictly ascending
800 sysadm 1.6 if (p_article_src->aid <= last_aid)
801 sysadm 1.1 {
802 sysadm 1.14 log_error("section_list_append_article(aid=%d) error: last_aid=%d\n", p_article_src->aid, last_aid);
803 sysadm 1.1 return -3;
804     }
805    
806 sysadm 1.7 p_article = (p_block->articles + p_block->article_count);
807     p_block->article_count++;
808     p_section->article_count++;
809    
810     // Copy article data
811     *p_article = *p_article_src;
812 sysadm 1.1
813 sysadm 1.8 if (p_article->visible)
814     {
815     p_section->visible_article_count++;
816     }
817    
818 sysadm 1.7 // Link appended article as tail node of topic bi-directional list
819     if (p_article->tid != 0)
820 sysadm 1.1 {
821 sysadm 1.7 p_topic_head = article_block_find_by_aid(p_article->tid);
822 sysadm 1.1 if (p_topic_head == NULL)
823     {
824 sysadm 1.7 log_error("search head of topic (aid=%d) error\n", p_article->tid);
825 sysadm 1.1 return -4;
826     }
827    
828 sysadm 1.6 p_topic_tail = p_topic_head->p_topic_prior;
829 sysadm 1.1 if (p_topic_tail == NULL)
830     {
831 sysadm 1.7 log_error("tail of topic (aid=%d) is NULL\n", p_article->tid);
832 sysadm 1.1 return -4;
833     }
834     }
835     else
836     {
837 sysadm 1.8 p_section->topic_count++;
838    
839     if (p_article->visible)
840     {
841     p_section->visible_topic_count++;
842     }
843    
844 sysadm 1.7 p_topic_head = p_article;
845     p_topic_tail = p_article;
846 sysadm 1.1 }
847    
848 sysadm 1.7 p_article->p_topic_prior = p_topic_tail;
849     p_article->p_topic_next = p_topic_head;
850     p_topic_head->p_topic_prior = p_article;
851     p_topic_tail->p_topic_next = p_article;
852 sysadm 1.1
853 sysadm 1.6 // Link appended article as tail node of article bi-directional list
854     if (p_section->p_article_head == NULL)
855     {
856     p_section->p_article_head = p_article;
857     p_section->p_article_tail = p_article;
858     }
859     p_article->p_prior = p_section->p_article_tail;
860     p_article->p_next = p_section->p_article_head;
861     p_section->p_article_head->p_prior = p_article;
862     p_section->p_article_tail->p_next = p_article;
863     p_section->p_article_tail = p_article;
864    
865 sysadm 1.7 // Update page
866 sysadm 1.50 if ((p_article->visible && p_section->last_page_visible_article_count == BBS_article_limit_per_page) ||
867     p_section->page_count == 0)
868 sysadm 1.1 {
869 sysadm 1.7 p_section->p_page_first_article[p_section->page_count] = p_article;
870     p_section->page_count++;
871 sysadm 1.8 p_section->last_page_visible_article_count = 0;
872 sysadm 1.1 }
873 sysadm 1.9
874     if (p_article->visible)
875     {
876     p_section->last_page_visible_article_count++;
877     }
878 sysadm 1.1
879 sysadm 1.35 if (p_article->ontop && section_list_update_article_ontop(p_section, p_article) < 0)
880     {
881     log_error("section_list_update_article_ontop(sid=%d, aid=%d) error\n",
882     p_section->sid, p_article->aid);
883     return -5;
884     }
885    
886 sysadm 1.7 return 0;
887 sysadm 1.2 }
888    
889 sysadm 1.7 int section_list_set_article_visible(SECTION_LIST *p_section, int32_t aid, int8_t visible)
890 sysadm 1.1 {
891     ARTICLE *p_article;
892 sysadm 1.8 ARTICLE *p_reply;
893     int affected_count = 0;
894 sysadm 1.1
895 sysadm 1.2 if (p_section == NULL)
896     {
897 sysadm 1.35 log_error("NULL pointer error\n");
898     return -1;
899 sysadm 1.2 }
900    
901 sysadm 1.7 p_article = article_block_find_by_aid(aid);
902 sysadm 1.1 if (p_article == NULL)
903     {
904     return -1; // Not found
905     }
906    
907 sysadm 1.14 if (p_section->sid != p_article->sid)
908     {
909 sysadm 1.35 log_error("Inconsistent section sid %d != article sid %d\n", p_section->sid, p_article->sid);
910 sysadm 1.14 return -2;
911     }
912    
913 sysadm 1.7 if (p_article->visible == visible)
914 sysadm 1.1 {
915 sysadm 1.7 return 0; // Already set
916 sysadm 1.1 }
917    
918 sysadm 1.8 if (visible == 0) // 1 -> 0
919     {
920     p_section->visible_article_count--;
921    
922 sysadm 1.46 if (user_article_cnt_inc(p_article->uid, -1) < 0)
923     {
924     log_error("user_article_cnt_inc(uid=%d, -1) error\n", p_article->uid);
925     }
926    
927 sysadm 1.8 if (p_article->tid == 0)
928     {
929     p_section->visible_topic_count--;
930    
931     // Set related visible replies to invisible
932     for (p_reply = p_article->p_topic_next; p_reply->tid != 0; p_reply = p_reply->p_topic_next)
933     {
934     if (p_reply->tid != aid)
935     {
936     log_error("Inconsistent tid = %d found in reply %d of topic %d\n", p_reply->tid, p_reply->aid, aid);
937     continue;
938     }
939    
940     if (p_reply->visible == 1)
941     {
942     p_reply->visible = 0;
943     p_section->visible_article_count--;
944     affected_count++;
945 sysadm 1.46
946     if (user_article_cnt_inc(p_reply->uid, -1) < 0)
947     {
948     log_error("user_article_cnt_inc(uid=%d, -1) error\n", p_reply->uid);
949     }
950 sysadm 1.8 }
951     }
952     }
953     }
954     else // 0 -> 1
955     {
956     p_section->visible_article_count++;
957    
958     if (p_article->tid == 0)
959     {
960     p_section->visible_topic_count++;
961     }
962 sysadm 1.46
963     if (user_article_cnt_inc(p_article->uid, 1) < 0)
964     {
965     log_error("user_article_cnt_inc(uid=%d, 1) error\n", p_article->uid);
966     }
967 sysadm 1.8 }
968    
969 sysadm 1.7 p_article->visible = visible;
970 sysadm 1.8 affected_count++;
971    
972     return affected_count;
973     }
974    
975 sysadm 1.35 int section_list_update_article_ontop(SECTION_LIST *p_section, ARTICLE *p_article)
976     {
977     int i;
978    
979     if (p_section == NULL || p_article == NULL)
980     {
981     log_error("NULL pointer error\n");
982     return -1;
983     }
984    
985     if (p_section->sid != p_article->sid)
986     {
987     log_error("Inconsistent section sid %d != article sid %d\n", p_section->sid, p_article->sid);
988     return -2;
989     }
990    
991     if (p_article->ontop)
992     {
993     for (i = 0; i < p_section->ontop_article_count; i++)
994     {
995     if (p_section->p_ontop_articles[i]->aid == p_article->aid)
996     {
997     log_error("Inconsistent state found: article %d already ontop in section %d\n", p_article->aid, p_section->sid);
998     return 0;
999     }
1000     else if (p_section->p_ontop_articles[i]->aid > p_article->aid)
1001     {
1002     break;
1003     }
1004     }
1005    
1006     // Remove the oldest one if the array of ontop articles is full
1007     if (p_section->ontop_article_count >= BBS_ontop_article_limit_per_section)
1008     {
1009     if (i == 0) // p_article is the oldest one
1010     {
1011     return 0;
1012     }
1013     memmove((void *)(p_section->p_ontop_articles),
1014     (void *)(p_section->p_ontop_articles + 1),
1015     sizeof(ARTICLE *) * (size_t)(i - 1));
1016     p_section->ontop_article_count--;
1017     i--;
1018     }
1019     else
1020     {
1021     memmove((void *)(p_section->p_ontop_articles + i + 1),
1022     (void *)(p_section->p_ontop_articles + i),
1023     sizeof(ARTICLE *) * (size_t)(p_section->ontop_article_count - i));
1024     }
1025    
1026     p_section->p_ontop_articles[i] = p_article;
1027     p_section->ontop_article_count++;
1028    
1029     // TODO: debug
1030     }
1031     else // ontop == 0
1032     {
1033     for (i = 0; i < p_section->ontop_article_count; i++)
1034     {
1035     if (p_section->p_ontop_articles[i]->aid == p_article->aid)
1036     {
1037     break;
1038     }
1039     }
1040     if (i == p_section->ontop_article_count) // not found
1041     {
1042     log_error("Inconsistent state found: article %d not ontop in section %d\n", p_article->aid, p_section->sid);
1043     return 0;
1044     }
1045    
1046     memmove((void *)(p_section->p_ontop_articles + i),
1047     (void *)(p_section->p_ontop_articles + i + 1),
1048     sizeof(ARTICLE *) * (size_t)(p_section->ontop_article_count - i - 1));
1049     p_section->ontop_article_count--;
1050     }
1051    
1052     return 0;
1053     }
1054    
1055     int section_list_page_count_with_ontop(SECTION_LIST *p_section)
1056     {
1057     int page_count;
1058    
1059     if (p_section == NULL)
1060     {
1061     log_error("NULL pointer error\n");
1062     return -1;
1063     }
1064    
1065 sysadm 1.50 page_count = p_section->page_count - 1 +
1066 sysadm 1.57 (p_section->last_page_visible_article_count + p_section->ontop_article_count + BBS_article_limit_per_page - 1) /
1067     BBS_article_limit_per_page;
1068 sysadm 1.50
1069     if (page_count < 0)
1070     {
1071     page_count = 0;
1072     }
1073 sysadm 1.35
1074     return page_count;
1075     }
1076    
1077     int section_list_page_article_count_with_ontop(SECTION_LIST *p_section, int32_t page_id)
1078     {
1079     if (p_section == NULL)
1080     {
1081     log_error("NULL pointer error\n");
1082     return -1;
1083     }
1084    
1085     if (page_id < p_section->page_count - 1)
1086     {
1087     return BBS_article_limit_per_page;
1088     }
1089     else // if (page_id >= p_section->page_count - 1)
1090     {
1091 sysadm 1.49 return MIN(MAX(0,
1092     (p_section->last_page_visible_article_count + p_section->ontop_article_count -
1093     BBS_article_limit_per_page * (page_id - p_section->page_count + 1))),
1094     BBS_article_limit_per_page);
1095 sysadm 1.35 }
1096     }
1097    
1098 sysadm 1.11 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)
1099 sysadm 1.8 {
1100     ARTICLE *p_article;
1101     int left;
1102     int right;
1103     int mid;
1104    
1105     *p_page = -1;
1106     *p_offset = -1;
1107 sysadm 1.11 *pp_next = NULL;
1108 sysadm 1.8
1109     if (p_section == NULL)
1110     {
1111 sysadm 1.42 log_error("NULL pointer error\n");
1112 sysadm 1.8 return NULL;
1113     }
1114 sysadm 1.7
1115 sysadm 1.8 if (p_section->article_count == 0) // empty
1116     {
1117     *p_page = 0;
1118     *p_offset = 0;
1119     return NULL;
1120     }
1121    
1122     left = 0;
1123 sysadm 1.47 right = p_section->page_count - 1;
1124 sysadm 1.8
1125 sysadm 1.47 // aid in the range [ head aid of pages[left], tail aid of pages[right] ]
1126     while (left < right)
1127 sysadm 1.8 {
1128     // get page id no less than mid value of left page id and right page id
1129 sysadm 1.47 mid = (left + right) / 2 + (left + right) % 2;
1130 sysadm 1.8
1131     if (aid < p_section->p_page_first_article[mid]->aid)
1132     {
1133 sysadm 1.47 right = mid - 1;
1134 sysadm 1.8 }
1135 sysadm 1.47 else // if (aid < p_section->p_page_first_article[mid]->aid)
1136 sysadm 1.8 {
1137     left = mid;
1138     }
1139     }
1140    
1141     *p_page = left;
1142    
1143     p_article = p_section->p_page_first_article[*p_page];
1144    
1145     // p_section->p_page_first_article[*p_page]->aid <= aid < p_section->p_page_first_article[*p_page + 1]->aid
1146 sysadm 1.11 right = (*p_page == MAX(0, p_section->page_count - 1) ? INT32_MAX : p_section->p_page_first_article[*p_page + 1]->aid);
1147 sysadm 1.8
1148     // left will be the offset of article found or offset to insert
1149     left = 0;
1150    
1151 sysadm 1.10 while (aid > p_article->aid)
1152 sysadm 1.8 {
1153     p_article = p_article->p_next;
1154     left++;
1155    
1156 sysadm 1.11 if (aid == p_article->aid)
1157     {
1158     *pp_next = p_article->p_next;
1159     break;
1160     }
1161    
1162 sysadm 1.8 // over last article in the page
1163     if (p_article == p_section->p_article_head || p_article->aid >= right)
1164     {
1165 sysadm 1.11 *pp_next = (p_article == p_section->p_article_head ? p_section->p_article_head : p_section->p_page_first_article[*p_page + 1]);
1166     *p_offset = left;
1167     return NULL; // not found
1168 sysadm 1.8 }
1169     }
1170    
1171 sysadm 1.11 if (aid < p_article->aid)
1172     {
1173     *pp_next = p_article;
1174     p_article = NULL; // not found
1175     }
1176     else // aid == p_article->aid
1177 sysadm 1.10 {
1178 sysadm 1.11 *pp_next = p_article->p_next;
1179 sysadm 1.10 }
1180    
1181 sysadm 1.8 *p_offset = left;
1182    
1183     return p_article;
1184     }
1185    
1186     int section_list_calculate_page(SECTION_LIST *p_section, int32_t start_aid)
1187     {
1188 sysadm 1.9 ARTICLE *p_article;
1189 sysadm 1.11 ARTICLE *p_next;
1190 sysadm 1.9 int32_t page;
1191     int32_t offset;
1192     int visible_article_count;
1193     int page_head_set;
1194 sysadm 1.8
1195     if (p_section == NULL)
1196     {
1197 sysadm 1.42 log_error("NULL pointer error\n");
1198 sysadm 1.8 return -1;
1199     }
1200    
1201 sysadm 1.11 if (p_section->article_count == 0) // empty
1202     {
1203     p_section->page_count = 0;
1204     p_section->last_page_visible_article_count = 0;
1205    
1206     return 0;
1207     }
1208    
1209     if (start_aid > 0)
1210 sysadm 1.9 {
1211 sysadm 1.14 p_article = article_block_find_by_aid(start_aid);
1212     if (p_article == NULL)
1213     {
1214     return -1; // Not found
1215     }
1216    
1217     if (p_section->sid != p_article->sid)
1218     {
1219     log_error("section_list_calculate_page() error: section sid %d != start article sid %d\n", p_section->sid, p_article->sid);
1220     return -2;
1221     }
1222    
1223 sysadm 1.11 p_article = section_list_find_article_with_offset(p_section, start_aid, &page, &offset, &p_next);
1224     if (p_article == NULL)
1225 sysadm 1.9 {
1226 sysadm 1.11 if (page < 0)
1227     {
1228     return -1;
1229     }
1230     log_error("section_list_calculate_page() aid = %d not found in section sid = %d\n",
1231     start_aid, p_section->sid);
1232     return -2;
1233 sysadm 1.9 }
1234    
1235 sysadm 1.11 if (offset > 0)
1236     {
1237     p_article = p_section->p_page_first_article[page];
1238     }
1239 sysadm 1.9 }
1240 sysadm 1.11 else
1241 sysadm 1.9 {
1242 sysadm 1.11 p_article = p_section->p_article_head;
1243     page = 0;
1244     offset = 0;
1245 sysadm 1.9 }
1246    
1247     visible_article_count = 0;
1248     page_head_set = 0;
1249    
1250     do
1251     {
1252     if (!page_head_set && visible_article_count == 0)
1253     {
1254     p_section->p_page_first_article[page] = p_article;
1255     page_head_set = 1;
1256     }
1257    
1258     if (p_article->visible)
1259     {
1260     visible_article_count++;
1261     }
1262    
1263     p_article = p_article->p_next;
1264    
1265     // skip remaining invisible articles
1266     while (p_article->visible == 0 && p_article != p_section->p_article_head)
1267     {
1268     p_article = p_article->p_next;
1269     }
1270    
1271     if (visible_article_count >= BBS_article_limit_per_page && p_article != p_section->p_article_head)
1272     {
1273     page++;
1274     visible_article_count = 0;
1275     page_head_set = 0;
1276    
1277     if (page >= BBS_article_limit_per_section / BBS_article_limit_per_page && p_article != p_section->p_article_head)
1278     {
1279     log_error("Count of page exceed limit in section %d\n", p_section->sid);
1280     break;
1281     }
1282     }
1283     } while (p_article != p_section->p_article_head);
1284    
1285     p_section->page_count = page + (visible_article_count > 0 ? 1 : 0);
1286 sysadm 1.50 p_section->last_page_visible_article_count = (visible_article_count > 0
1287     ? visible_article_count
1288     : (page > 0 ? BBS_article_limit_per_page : 0));
1289 sysadm 1.9
1290 sysadm 1.8 return 0;
1291 sysadm 1.1 }
1292 sysadm 1.11
1293 sysadm 1.25 int32_t article_block_last_aid(void)
1294     {
1295     ARTICLE_BLOCK *p_block = p_article_block_pool->p_block[p_article_block_pool->block_count - 1];
1296     int32_t last_aid = p_block->articles[p_block->article_count - 1].aid;
1297    
1298     return last_aid;
1299     }
1300    
1301 sysadm 1.27 int article_block_article_count(void)
1302     {
1303     int ret;
1304    
1305     if (p_article_block_pool == NULL || p_article_block_pool->block_count <= 0)
1306     {
1307     return -1;
1308     }
1309    
1310 sysadm 1.54 ret = (p_article_block_pool->block_count - 1) * BBS_article_count_per_block +
1311 sysadm 1.27 p_article_block_pool->p_block[p_article_block_pool->block_count - 1]->article_count;
1312    
1313     return ret;
1314     }
1315    
1316 sysadm 1.14 int article_count_of_topic(int32_t aid)
1317 sysadm 1.11 {
1318     ARTICLE *p_article;
1319     int article_count;
1320    
1321     p_article = article_block_find_by_aid(aid);
1322     if (p_article == NULL)
1323     {
1324     return 0; // Not found
1325     }
1326    
1327     article_count = 0;
1328    
1329     do
1330     {
1331 sysadm 1.15 if (p_article->tid != 0 && p_article->tid != aid)
1332     {
1333     log_error("article_count_of_topic(%d) error: article %d not linked to the topic\n", aid, p_article->aid);
1334     break;
1335     }
1336    
1337 sysadm 1.11 article_count++;
1338     p_article = p_article->p_topic_next;
1339     } while (p_article->aid != aid);
1340    
1341     return article_count;
1342     }
1343    
1344     int section_list_move_topic(SECTION_LIST *p_section_src, SECTION_LIST *p_section_dest, int32_t aid)
1345     {
1346     ARTICLE *p_article;
1347     ARTICLE *p_next;
1348     int32_t page;
1349     int32_t offset;
1350     int32_t move_article_count;
1351     int32_t dest_article_count_old;
1352     int32_t last_unaffected_aid_src;
1353 sysadm 1.12 int32_t first_inserted_aid_dest;
1354     int move_counter;
1355 sysadm 1.11
1356 sysadm 1.28 if (p_section_src == NULL || p_section_dest == NULL)
1357 sysadm 1.11 {
1358 sysadm 1.42 log_error("NULL pointer error\n");
1359 sysadm 1.11 return -1;
1360     }
1361    
1362 sysadm 1.28 if (p_section_src->sid == p_section_dest->sid)
1363     {
1364     log_error("section_list_move_topic() src and dest section are the same\n");
1365     return -1;
1366     }
1367    
1368 sysadm 1.14 if ((p_article = article_block_find_by_aid(aid)) == NULL)
1369 sysadm 1.11 {
1370 sysadm 1.28 log_error("article_block_find_by_aid(aid = %d) error: article not found\n", aid);
1371 sysadm 1.14 return -2;
1372     }
1373    
1374     if (p_section_src->sid != p_article->sid)
1375     {
1376     log_error("section_list_move_topic() error: src section sid %d != article %d sid %d\n",
1377     p_section_src->sid, p_article->aid, p_article->sid);
1378 sysadm 1.11 return -2;
1379     }
1380    
1381     if (p_article->tid != 0)
1382     {
1383     log_error("section_list_move_topic(aid = %d) error: article is not head of topic, tid = %d\n", aid, p_article->tid);
1384     return -2;
1385     }
1386    
1387     last_unaffected_aid_src = (p_article == p_section_src->p_article_head ? 0 : p_article->p_prior->aid);
1388    
1389 sysadm 1.14 move_article_count = article_count_of_topic(aid);
1390 sysadm 1.11 if (move_article_count <= 0)
1391     {
1392 sysadm 1.22 log_error("article_count_of_topic(aid = %d) <= 0\n", aid);
1393 sysadm 1.11 return -2;
1394     }
1395    
1396     if (p_section_dest->article_count + move_article_count > BBS_article_limit_per_section)
1397     {
1398     log_error("section_list_move_topic() error: article_count %d reach limit in section %d\n",
1399     p_section_dest->article_count + move_article_count, p_section_dest->sid);
1400     return -3;
1401     }
1402    
1403     dest_article_count_old = p_section_dest->article_count;
1404 sysadm 1.12 move_counter = 0;
1405     first_inserted_aid_dest = p_article->aid;
1406 sysadm 1.11
1407     do
1408     {
1409 sysadm 1.14 if (p_section_src->sid != p_article->sid)
1410 sysadm 1.11 {
1411 sysadm 1.28 log_error("section_list_move_topic() warning: src section sid %d != article %d sid %d\n",
1412 sysadm 1.14 p_section_src->sid, p_article->aid, p_article->sid);
1413 sysadm 1.28 p_article = p_article->p_topic_next;
1414     continue;
1415 sysadm 1.11 }
1416    
1417     // Remove from bi-directional article list of src section
1418     if (p_section_src->p_article_head == p_article)
1419     {
1420     p_section_src->p_article_head = p_article->p_next;
1421     }
1422     if (p_section_src->p_article_tail == p_article)
1423     {
1424     p_section_src->p_article_tail = p_article->p_prior;
1425     }
1426     if (p_section_src->p_article_head == p_article) // || p_section_src->p_article_tail == p_article
1427     {
1428     p_section_src->p_article_head = NULL;
1429     p_section_src->p_article_tail = NULL;
1430     }
1431    
1432     p_article->p_prior->p_next = p_article->p_next;
1433     p_article->p_next->p_prior = p_article->p_prior;
1434    
1435 sysadm 1.14 // Update sid of article
1436     p_article->sid = p_section_dest->sid;
1437    
1438     if (section_list_find_article_with_offset(p_section_dest, p_article->aid, &page, &offset, &p_next) != NULL)
1439     {
1440 sysadm 1.28 log_error("section_list_move_topic() warning: article %d already in section %d\n", p_article->aid, p_section_dest->sid);
1441     p_article = p_article->p_topic_next;
1442     continue;
1443 sysadm 1.14 }
1444    
1445 sysadm 1.11 // Insert into bi-directional article list of dest section
1446     if (p_next == NULL) // empty section
1447     {
1448     p_section_dest->p_article_head = p_article;
1449     p_section_dest->p_article_tail = p_article;
1450     p_article->p_prior = p_article;
1451     p_article->p_next = p_article;
1452     }
1453     else
1454     {
1455     if (p_section_dest->p_article_head == p_next)
1456     {
1457     if (p_article->aid < p_next->aid)
1458     {
1459     p_section_dest->p_article_head = p_article;
1460     }
1461     else // p_article->aid > p_next->aid
1462     {
1463     p_section_dest->p_article_tail = p_article;
1464     }
1465     }
1466    
1467     p_article->p_prior = p_next->p_prior;
1468     p_article->p_next = p_next;
1469     p_next->p_prior->p_next = p_article;
1470     p_next->p_prior = p_article;
1471     }
1472    
1473     // Update article / topic counter of src / desc section
1474     p_section_src->article_count--;
1475     p_section_dest->article_count++;
1476     if (p_article->tid == 0)
1477     {
1478     p_section_src->topic_count--;
1479     p_section_dest->topic_count++;
1480     }
1481    
1482     // Update visible article / topic counter of src / desc section
1483     if (p_article->visible)
1484     {
1485     p_section_src->visible_article_count--;
1486     p_section_dest->visible_article_count++;
1487     if (p_article->tid == 0)
1488     {
1489     p_section_src->visible_topic_count--;
1490     p_section_dest->visible_topic_count++;
1491     }
1492     }
1493    
1494     // Update page for empty dest section
1495     if (p_section_dest->article_count == 1)
1496     {
1497     p_section_dest->p_page_first_article[0] = p_article;
1498     p_section_dest->page_count = 1;
1499     p_section_dest->last_page_visible_article_count = (p_article->visible ? 1 : 0);
1500     }
1501    
1502     p_article = p_article->p_topic_next;
1503 sysadm 1.12
1504     move_counter++;
1505     if (move_counter % CALCULATE_PAGE_THRESHOLD == 0)
1506     {
1507     // Re-calculate pages of desc section
1508     if (section_list_calculate_page(p_section_dest, first_inserted_aid_dest) < 0)
1509     {
1510 sysadm 1.14 log_error("section_list_calculate_page(dest section = %d, aid = %d) error\n",
1511 sysadm 1.12 p_section_dest->sid, first_inserted_aid_dest);
1512     }
1513    
1514     first_inserted_aid_dest = p_article->aid;
1515     }
1516 sysadm 1.11 } while (p_article->aid != aid);
1517    
1518     if (p_section_dest->article_count - dest_article_count_old != move_article_count)
1519     {
1520 sysadm 1.28 log_error("section_list_move_topic() warning: count of moved articles %d != %d\n",
1521 sysadm 1.11 p_section_dest->article_count - dest_article_count_old, move_article_count);
1522     }
1523    
1524 sysadm 1.12 // Re-calculate pages of src section
1525 sysadm 1.11 if (section_list_calculate_page(p_section_src, last_unaffected_aid_src) < 0)
1526     {
1527 sysadm 1.14 log_error("section_list_calculate_page(src section = %d, aid = %d) error at aid = %d\n",
1528 sysadm 1.12 p_section_src->sid, last_unaffected_aid_src, aid);
1529 sysadm 1.11 }
1530 sysadm 1.12
1531     if (move_counter % CALCULATE_PAGE_THRESHOLD != 0)
1532 sysadm 1.11 {
1533 sysadm 1.12 // Re-calculate pages of desc section
1534     if (section_list_calculate_page(p_section_dest, first_inserted_aid_dest) < 0)
1535     {
1536 sysadm 1.14 log_error("section_list_calculate_page(dest section = %d, aid = %d) error\n",
1537 sysadm 1.12 p_section_dest->sid, first_inserted_aid_dest);
1538     }
1539 sysadm 1.11 }
1540    
1541     return move_article_count;
1542     }
1543 sysadm 1.16
1544     int get_section_index(SECTION_LIST *p_section)
1545     {
1546     int index;
1547    
1548     if (p_section_list_pool == NULL)
1549     {
1550     log_error("get_section_index() error: uninitialized\n");
1551     return -1;
1552     }
1553    
1554     if (p_section == NULL)
1555     {
1556     index = BBS_max_section;
1557     }
1558     else
1559     {
1560 sysadm 1.22 index = (int)(p_section - p_section_list_pool->sections);
1561 sysadm 1.16 if (index < 0 || index >= BBS_max_section)
1562     {
1563     log_error("get_section_index(%d) error: index out of range\n", index);
1564     return -2;
1565     }
1566     }
1567    
1568     return index;
1569     }
1570    
1571 sysadm 1.48 int get_section_info(SECTION_LIST *p_section, char *sname, char *stitle, char *master_list)
1572     {
1573     if (p_section == NULL)
1574     {
1575     log_error("NULL pointer error\n");
1576     return -1;
1577     }
1578    
1579     if (section_list_rd_lock(p_section) < 0)
1580     {
1581     log_error("section_list_rd_lock(sid=%d) error\n", p_section->sid);
1582     return -2;
1583     }
1584    
1585     if (sname != NULL)
1586     {
1587     memcpy(sname, p_section->sname, sizeof(p_section->sname));
1588     }
1589     if (stitle != NULL)
1590     {
1591     memcpy(stitle, p_section->stitle, sizeof(p_section->stitle));
1592     }
1593     if (master_list != NULL)
1594     {
1595     memcpy(master_list, p_section->master_list, sizeof(p_section->master_list));
1596     }
1597    
1598     // release lock of section
1599     if (section_list_rd_unlock(p_section) < 0)
1600     {
1601     log_error("section_list_rd_unlock(sid=%d) error\n", p_section->sid);
1602     return -2;
1603     }
1604    
1605     return 0;
1606     }
1607    
1608 sysadm 1.16 int section_list_try_rd_lock(SECTION_LIST *p_section, int wait_sec)
1609     {
1610     int index;
1611 sysadm 1.17 struct sembuf sops[4];
1612 sysadm 1.58 #if !defined(__MSYS__) && !defined(__MINGW32__)
1613 sysadm 1.16 struct timespec timeout;
1614 sysadm 1.58 #endif
1615 sysadm 1.16 int ret;
1616    
1617     index = get_section_index(p_section);
1618     if (index < 0)
1619     {
1620     return -2;
1621     }
1622    
1623 sysadm 1.17 sops[0].sem_num = (unsigned short)(index * 2 + 1); // w_sem of section index
1624     sops[0].sem_op = 0; // wait until unlocked
1625 sysadm 1.16 sops[0].sem_flg = 0;
1626    
1627 sysadm 1.17 sops[1].sem_num = (unsigned short)(index * 2); // r_sem of section index
1628     sops[1].sem_op = 1; // lock
1629     sops[1].sem_flg = SEM_UNDO; // undo on terminate
1630    
1631     // Read lock on any specific section will also acquire single read lock on "all section"
1632     // so that write lock on all section only need to acquire single write on on "all section"
1633     // rather than to acquire multiple write locks on all the available sections.
1634 sysadm 1.21 if (index != BBS_max_section)
1635 sysadm 1.17 {
1636     sops[2].sem_num = BBS_max_section * 2 + 1; // w_sem of all section
1637     sops[2].sem_op = 0; // wait until unlocked
1638     sops[2].sem_flg = 0;
1639    
1640     sops[3].sem_num = BBS_max_section * 2; // r_sem of all section
1641     sops[3].sem_op = 1; // lock
1642     sops[3].sem_flg = SEM_UNDO; // undo on terminate
1643     }
1644 sysadm 1.16
1645 sysadm 1.58 #if defined(__MSYS__) || defined(__MINGW32__)
1646     ret = semop(p_section_list_pool->semid, sops, (index == BBS_max_section ? 2 : 4));
1647     #else
1648 sysadm 1.16 timeout.tv_sec = wait_sec;
1649     timeout.tv_nsec = 0;
1650    
1651 sysadm 1.22 ret = semtimedop(p_section_list_pool->semid, sops, (index == BBS_max_section ? 2 : 4), &timeout);
1652 sysadm 1.58 #endif
1653 sysadm 1.17 if (ret == -1 && errno != EAGAIN && errno != EINTR)
1654     {
1655 sysadm 1.58 log_error("semop(index = %d, lock read) error %d\n", index, errno);
1656 sysadm 1.17 }
1657 sysadm 1.16
1658     return ret;
1659     }
1660    
1661     int section_list_try_rw_lock(SECTION_LIST *p_section, int wait_sec)
1662     {
1663     int index;
1664     struct sembuf sops[3];
1665 sysadm 1.58 #if !defined(__MSYS__) && !defined(__MINGW32__)
1666 sysadm 1.16 struct timespec timeout;
1667 sysadm 1.58 #endif
1668 sysadm 1.16 int ret;
1669    
1670     index = get_section_index(p_section);
1671     if (index < 0)
1672     {
1673     return -2;
1674     }
1675    
1676 sysadm 1.17 sops[0].sem_num = (unsigned short)(index * 2 + 1); // w_sem of section index
1677     sops[0].sem_op = 0; // wait until unlocked
1678 sysadm 1.16 sops[0].sem_flg = 0;
1679    
1680 sysadm 1.17 sops[1].sem_num = (unsigned short)(index * 2 + 1); // w_sem of section index
1681     sops[1].sem_op = 1; // lock
1682     sops[1].sem_flg = SEM_UNDO; // undo on terminate
1683    
1684     sops[2].sem_num = (unsigned short)(index * 2); // r_sem of section index
1685     sops[2].sem_op = 0; // wait until unlocked
1686     sops[2].sem_flg = 0;
1687 sysadm 1.16
1688 sysadm 1.58 #if defined(__MSYS__) || defined(__MINGW32__)
1689     ret = semop(p_section_list_pool->semid, sops, 3);
1690     #else
1691 sysadm 1.16 timeout.tv_sec = wait_sec;
1692     timeout.tv_nsec = 0;
1693    
1694 sysadm 1.22 ret = semtimedop(p_section_list_pool->semid, sops, 3, &timeout);
1695 sysadm 1.58 #endif
1696 sysadm 1.17 if (ret == -1 && errno != EAGAIN && errno != EINTR)
1697     {
1698 sysadm 1.58 log_error("semop(index = %d, lock write) error %d\n", index, errno);
1699 sysadm 1.17 }
1700 sysadm 1.16
1701     return ret;
1702     }
1703    
1704     int section_list_rd_unlock(SECTION_LIST *p_section)
1705     {
1706     int index;
1707 sysadm 1.17 struct sembuf sops[2];
1708 sysadm 1.16 int ret;
1709    
1710     index = get_section_index(p_section);
1711     if (index < 0)
1712     {
1713     return -2;
1714     }
1715    
1716 sysadm 1.17 sops[0].sem_num = (unsigned short)(index * 2); // r_sem of section index
1717     sops[0].sem_op = -1; // unlock
1718     sops[0].sem_flg = IPC_NOWAIT | SEM_UNDO; // no wait
1719 sysadm 1.16
1720 sysadm 1.21 if (index != BBS_max_section)
1721 sysadm 1.17 {
1722     sops[1].sem_num = BBS_max_section * 2; // r_sem of all section
1723     sops[1].sem_op = -1; // unlock
1724     sops[1].sem_flg = IPC_NOWAIT | SEM_UNDO; // no wait
1725     }
1726    
1727 sysadm 1.22 ret = semop(p_section_list_pool->semid, sops, (index == BBS_max_section ? 1 : 2));
1728 sysadm 1.17 if (ret == -1 && errno != EAGAIN && errno != EINTR)
1729     {
1730     log_error("semop(index = %d, unlock read) error %d\n", index, errno);
1731     }
1732 sysadm 1.16
1733     return ret;
1734     }
1735    
1736     int section_list_rw_unlock(SECTION_LIST *p_section)
1737     {
1738     int index;
1739     struct sembuf sops[1];
1740     int ret;
1741    
1742     index = get_section_index(p_section);
1743     if (index < 0)
1744     {
1745     return -2;
1746     }
1747    
1748 sysadm 1.17 sops[0].sem_num = (unsigned short)(index * 2 + 1); // w_sem of section index
1749     sops[0].sem_op = -1; // unlock
1750     sops[0].sem_flg = IPC_NOWAIT | SEM_UNDO; // no wait
1751 sysadm 1.16
1752 sysadm 1.22 ret = semop(p_section_list_pool->semid, sops, 1);
1753 sysadm 1.17 if (ret == -1 && errno != EAGAIN && errno != EINTR)
1754     {
1755     log_error("semop(index = %d, unlock write) error %d\n", index, errno);
1756     }
1757 sysadm 1.16
1758     return ret;
1759     }
1760 sysadm 1.23
1761     int section_list_rd_lock(SECTION_LIST *p_section)
1762     {
1763     int timer = 0;
1764     int sid = (p_section == NULL ? 0 : p_section->sid);
1765     int ret = -1;
1766    
1767     while (!SYS_server_exit)
1768     {
1769     ret = section_list_try_rd_lock(p_section, SECTION_TRY_LOCK_WAIT_TIME);
1770     if (ret == 0) // success
1771     {
1772     break;
1773     }
1774     else if (errno == EAGAIN || errno == EINTR) // retry
1775     {
1776     timer++;
1777     if (timer % SECTION_TRY_LOCK_TIMES == 0)
1778     {
1779 sysadm 1.45 log_error("section_list_try_rd_lock() tried %d times on section %d\n", timer, sid);
1780 sysadm 1.23 }
1781     }
1782     else // failed
1783     {
1784 sysadm 1.37 log_error("section_list_try_rd_lock() failed on section %d\n", sid);
1785 sysadm 1.23 break;
1786     }
1787     }
1788    
1789     return ret;
1790     }
1791    
1792     int section_list_rw_lock(SECTION_LIST *p_section)
1793     {
1794     int timer = 0;
1795     int sid = (p_section == NULL ? 0 : p_section->sid);
1796     int ret = -1;
1797    
1798     while (!SYS_server_exit)
1799     {
1800     ret = section_list_try_rw_lock(p_section, SECTION_TRY_LOCK_WAIT_TIME);
1801     if (ret == 0) // success
1802     {
1803     break;
1804     }
1805     else if (errno == EAGAIN || errno == EINTR) // retry
1806     {
1807     timer++;
1808     if (timer % SECTION_TRY_LOCK_TIMES == 0)
1809     {
1810 sysadm 1.45 log_error("section_list_try_rw_lock() tried %d times on section %d\n", timer, sid);
1811 sysadm 1.23 }
1812     }
1813     else // failed
1814     {
1815 sysadm 1.37 log_error("section_list_try_rw_lock() failed on section %d\n", sid);
1816 sysadm 1.23 break;
1817     }
1818     }
1819    
1820     return ret;
1821     }

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