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

Contents of /lbbs/src/menu.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.62 - (show annotations)
Fri May 30 02:57:09 2025 UTC (9 months, 2 weeks ago) by sysadm
Branch: MAIN
Changes since 1.61: +31 -4 lines
Content type: text/x-csrc
Add s_favor based filter

1 /***************************************************************************
2 menu.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 "bbs.h"
18 #include "bbs_cmd.h"
19 #include "user_priv.h"
20 #include "bbs_cmd.h"
21 #include "menu.h"
22 #include "log.h"
23 #include "io.h"
24 #include "screen.h"
25 #include "common.h"
26 #include <string.h>
27 #include <stdio.h>
28 #include <ctype.h>
29 #include <stdlib.h>
30 #include <errno.h>
31 #include <unistd.h>
32 #include <sys/shm.h>
33 #include <sys/ipc.h>
34
35 #define MENU_SCREEN_PATH_PREFIX "var/MENU_SCR_"
36 #define MENU_CONF_DELIM_WITH_SPACE " ,\t\r\n"
37 #define MENU_CONF_DELIM_WITHOUT_SPACE "\r\n"
38
39 #define MENU_SET_RESERVED_LENGTH (sizeof(int16_t) * 4)
40
41 MENU_SET *p_bbs_menu;
42
43 int load_menu(MENU_SET *p_menu_set, const char *conf_file)
44 {
45 FILE *fin;
46 int fin_line = 0;
47 char buffer[LINE_BUFFER_LEN];
48 char temp[LINE_BUFFER_LEN];
49 char *p = NULL;
50 char *q = NULL;
51 char *r = NULL;
52 char *saveptr = NULL;
53 MENU *p_menu = NULL;
54 MENU_ITEM *p_menu_item = NULL;
55 MENU_SCREEN *p_screen = NULL;
56 MENU_ID menu_id;
57 MENU_ITEM_ID menu_item_id;
58 MENU_SCREEN_ID screen_id;
59 int proj_id;
60 key_t key;
61 size_t size;
62
63 // Use trie_dict to search menu_id by menu name
64 p_menu_set->p_menu_name_dict = trie_dict_create();
65 if (p_menu_set->p_menu_name_dict == NULL)
66 {
67 log_error("trie_dict_create() error\n");
68 return -3;
69 }
70
71 // Use trie_dict to search screen_id by menu screen name
72 p_menu_set->p_menu_screen_dict = trie_dict_create();
73 if (p_menu_set->p_menu_screen_dict == NULL)
74 {
75 log_error("trie_dict_create() error\n");
76 return -3;
77 }
78
79 if ((fin = fopen(conf_file, "r")) == NULL)
80 {
81 log_error("Open %s failed", conf_file);
82 return -2;
83 }
84
85 // Allocate shared memory
86 proj_id = (int)(time(NULL) % getpid());
87 key = ftok(conf_file, proj_id);
88 if (key == -1)
89 {
90 log_error("ftok(%s %d) error (%d)\n", conf_file, proj_id, errno);
91 return -2;
92 }
93
94 size = MENU_SET_RESERVED_LENGTH +
95 sizeof(MENU) * MAX_MENUS +
96 sizeof(MENU_ITEM) * MAX_MENUITEMS +
97 sizeof(MENU_SCREEN) * MAX_MENUS +
98 MAX_MENU_SCR_BUF_LENGTH * MAX_MENUS;
99 p_menu_set->shmid = shmget(key, size, IPC_CREAT | IPC_EXCL | 0600);
100 if (p_menu_set->shmid == -1)
101 {
102 log_error("shmget(size = %d) error (%d)\n", size, errno);
103 return -3;
104 }
105 p_menu_set->p_reserved = shmat(p_menu_set->shmid, NULL, 0);
106 if (p_menu_set->p_reserved == (void *)-1)
107 {
108 log_error("shmat() error (%d)\n", errno);
109 return -3;
110 }
111 p_menu_set->p_menu_pool = p_menu_set->p_reserved + MENU_SET_RESERVED_LENGTH;
112 p_menu_set->p_menu_item_pool = p_menu_set->p_menu_pool + sizeof(MENU) * MAX_MENUS;
113 p_menu_set->p_menu_screen_pool = p_menu_set->p_menu_item_pool + sizeof(MENU_ITEM) * MAX_MENUITEMS;
114 p_menu_set->p_menu_screen_buf = p_menu_set->p_menu_screen_pool + sizeof(MENU_SCREEN) * MAX_MENUS;
115 p_menu_set->p_menu_screen_buf_free = p_menu_set->p_menu_screen_buf;
116
117 p_menu_set->menu_count = 0;
118 p_menu_set->menu_item_count = 0;
119 p_menu_set->menu_screen_count = 0;
120 p_menu_set->choose_step = 0;
121 p_menu_set->menu_id_path[0] = 0;
122
123 while (fgets(buffer, sizeof(buffer), fin))
124 {
125 fin_line++;
126
127 p = strtok_r(buffer, MENU_CONF_DELIM_WITH_SPACE, &saveptr);
128 if (p == NULL) // Blank line
129 {
130 continue;
131 }
132
133 if (*p == '#' || *p == '\r' || *p == '\n') // Comment or blank line
134 {
135 continue;
136 }
137
138 if (*p == '%')
139 {
140 p++;
141
142 if (strcmp(p, "menu") == 0) // BEGIN of sub-menu
143 {
144 if (p_menu != NULL)
145 {
146 log_error("Incomplete menu definition in menu config line %d\n", fin_line);
147 return -1;
148 }
149
150 if (p_menu_set->menu_count >= MAX_MENUS)
151 {
152 log_error("Menu count (%d) exceed limit (%d)\n", p_menu_set->menu_count, MAX_MENUS);
153 return -3;
154 }
155 menu_id = (MENU_ID)p_menu_set->menu_count;
156 p_menu_set->menu_count++;
157
158 p_menu = get_menu_by_id(p_menu_set, menu_id);
159
160 p_menu->item_count = 0;
161 p_menu->title.show = 0;
162 p_menu->screen_show = 0;
163 p_menu->page_item_limit = 0;
164 p_menu->use_filter = 0;
165 p_menu->filter_handler = NULL;
166
167 q = strtok_r(NULL, MENU_CONF_DELIM_WITH_SPACE, &saveptr);
168 if (q == NULL)
169 {
170 log_error("Error menu name in menu config line %d\n", fin_line);
171 return -1;
172 }
173 p = q;
174 while (isalnum(*q) || *q == '_' || *q == '-')
175 {
176 q++;
177 }
178 if (*q != '\0')
179 {
180 log_error("Error menu name in menu config line %d\n", fin_line);
181 return -1;
182 }
183
184 if (q - p > sizeof(p_menu->name) - 1)
185 {
186 log_error("Too longer menu name in menu config line %d\n", fin_line);
187 return -1;
188 }
189 strncpy(p_menu->name, p, sizeof(p_menu->name) - 1);
190 p_menu->name[sizeof(p_menu->name) - 1] = '\0';
191
192 if (trie_dict_set(p_menu_set->p_menu_name_dict, p_menu->name, (int64_t)menu_id) != 1)
193 {
194 log_error("Error set menu dict [%s]\n", p_menu->name);
195 }
196
197 // Check syntax
198 q = strtok_r(NULL, MENU_CONF_DELIM_WITH_SPACE, &saveptr);
199 if (q != NULL)
200 {
201 log_error("Unknown extra content in menu config line %d\n", fin_line);
202 return -1;
203 }
204
205 while (fgets(buffer, sizeof(buffer), fin))
206 {
207 fin_line++;
208
209 p = strtok_r(buffer, MENU_CONF_DELIM_WITH_SPACE, &saveptr);
210 if (p == NULL) // Blank line
211 {
212 continue;
213 }
214
215 if (*p == '#' || *p == '\r' || *p == '\n') // Comment or blank line
216 {
217 continue;
218 }
219
220 if (*p == '%') // END of sub-menu
221 {
222 p_menu = NULL;
223 break;
224 }
225 else if (*p == '!' || *p == '@')
226 {
227 // BEGIN of menu item
228 if (p_menu->item_count >= MAX_ITEMS_PER_MENU)
229 {
230 log_error("Menuitem count per menu (%d) exceed limit (%d)\n", p_menu->item_count, MAX_ITEMS_PER_MENU);
231 return -1;
232 }
233 if (p_menu_set->menu_item_count >= MAX_MENUITEMS)
234 {
235 log_error("Menuitem count (%d) exceed limit (%d)\n", p_menu_set->menu_item_count, MAX_MENUITEMS);
236 return -3;
237 }
238 menu_item_id = (MENU_ITEM_ID)p_menu_set->menu_item_count;
239 p_menu_set->menu_item_count++;
240
241 p_menu->items[p_menu->item_count] = menu_item_id;
242 p_menu->item_count++;
243
244 p_menu_item = get_menu_item_by_id(p_menu_set, menu_item_id);
245
246 p_menu_item->submenu = (*p == '!' ? 1 : 0);
247
248 // Menu item action
249 p++;
250 if (strcmp(p, "..") == 0) // Return to parent menu
251 {
252 q = p + 2; // strlen("..")
253 }
254 else
255 {
256 q = p;
257 while (isalnum(*q) || *q == '_' || *q == '-')
258 {
259 q++;
260 }
261 if (*q != '\0')
262 {
263 log_error("Error menu item action in menu config line %d\n", fin_line);
264 return -1;
265 }
266 }
267
268 if (q - p > sizeof(p_menu_item->action) - 1)
269 {
270 log_error("Too longer menu action in menu config line %d\n", fin_line);
271 return -1;
272 }
273 strncpy(p_menu_item->action, p, sizeof(p_menu_item->action) - 1);
274 p_menu_item->action[sizeof(p_menu_item->action) - 1] = '\0';
275
276 // Menu item row
277 q = strtok_r(NULL, MENU_CONF_DELIM_WITH_SPACE, &saveptr);
278 if (q == NULL)
279 {
280 log_error("Error menu item row in menu config line %d\n", fin_line);
281 return -1;
282 }
283 p = q;
284 while (isdigit(*q))
285 {
286 q++;
287 }
288 if (*q != '\0')
289 {
290 log_error("Error menu item row in menu config line %d\n", fin_line);
291 return -1;
292 }
293 p_menu_item->row = (int16_t)atoi(p);
294
295 // Menu item col
296 q = strtok_r(NULL, MENU_CONF_DELIM_WITH_SPACE, &saveptr);
297 if (q == NULL)
298 {
299 log_error("Error menu item col in menu config line %d\n", fin_line);
300 return -1;
301 }
302 p = q;
303 while (isdigit(*q))
304 {
305 q++;
306 }
307 if (*q != '\0')
308 {
309 log_error("Error menu item col in menu config line %d\n", fin_line);
310 return -1;
311 }
312 p_menu_item->col = (int16_t)atoi(p);
313
314 // Menu item priv
315 q = strtok_r(NULL, MENU_CONF_DELIM_WITH_SPACE, &saveptr);
316 if (q == NULL)
317 {
318 log_error("Error menu item priv in menu config line %d\n", fin_line);
319 return -1;
320 }
321 p = q;
322 while (isdigit(*q))
323 {
324 q++;
325 }
326 if (*q != '\0')
327 {
328 log_error("Error menu item priv in menu config line %d\n", fin_line);
329 return -1;
330 }
331 p_menu_item->priv = atoi(p);
332
333 // Menu item level
334 q = strtok_r(NULL, MENU_CONF_DELIM_WITH_SPACE, &saveptr);
335 if (q == NULL)
336 {
337 log_error("Error menu item level in menu config line %d\n", fin_line);
338 return -1;
339 }
340 p = q;
341 while (isdigit(*q))
342 {
343 q++;
344 }
345 if (*q != '\0')
346 {
347 log_error("Error menu item level in menu config line %d\n", fin_line);
348 return -1;
349 }
350 p_menu_item->level = atoi(p);
351
352 // Menu item name
353 q = strtok_r(NULL, MENU_CONF_DELIM_WITH_SPACE, &saveptr);
354 if (q == NULL || *q != '\"')
355 {
356 log_error("Error menu item name in menu config line %d\n", fin_line);
357 return -1;
358 }
359 q++;
360 p = q;
361 while (*q != '\0' && *q != '\"')
362 {
363 if (*q == '\\')
364 {
365 r = q;
366 while (*r != '\0')
367 {
368 *r = *(r + 1);
369 r++;
370 }
371 }
372 q++;
373 }
374 if (*q != '\"' || *(q + 1) != '\0')
375 {
376 log_error("Error menu item name in menu config line %d\n", fin_line);
377 return -1;
378 }
379 *q = '\0';
380
381 if (q - p > sizeof(p_menu_item->name) - 1)
382 {
383 log_error("Too longer menu name in menu config line %d\n", fin_line);
384 return -1;
385 }
386 strncpy(p_menu_item->name, p, sizeof(p_menu_item->name) - 1);
387 p_menu_item->name[sizeof(p_menu_item->name) - 1] = '\0';
388
389 // Menu item text
390 q = strtok_r(NULL, MENU_CONF_DELIM_WITHOUT_SPACE, &saveptr);
391 if (q == NULL || (q = strchr(q, '\"')) == NULL)
392 {
393 log_error("Error menu item text in menu config line %d\n", fin_line);
394 return -1;
395 }
396 q++;
397 p = q;
398 while (*q != '\0' && *q != '\"')
399 {
400 if (*q == '\\')
401 {
402 r = q;
403 while (*r != '\0')
404 {
405 *r = *(r + 1);
406 r++;
407 }
408 }
409 q++;
410 }
411 if (*q != '\"')
412 {
413 log_error("Error menu item text in menu config line %d\n", fin_line);
414 return -1;
415 }
416 *q = '\0';
417
418 if (q - p > sizeof(p_menu_item->text) - 1)
419 {
420 log_error("Too longer menu item text in menu config line %d\n", fin_line);
421 return -1;
422 }
423 strncpy(p_menu_item->text, p, sizeof(p_menu_item->text) - 1);
424 p_menu_item->text[sizeof(p_menu_item->text) - 1] = '\0';
425
426 // Check syntax
427 q = strtok_r(q + 1, MENU_CONF_DELIM_WITH_SPACE, &saveptr);
428 if (q != NULL)
429 {
430 log_error("Unknown extra content in menu config line %d\n", fin_line);
431 return -1;
432 }
433 }
434 else if (strcmp(p, "title") == 0)
435 {
436 p_menu->title.show = 1;
437
438 // Menu title row
439 q = strtok_r(NULL, MENU_CONF_DELIM_WITH_SPACE, &saveptr);
440 if (q == NULL)
441 {
442 log_error("Error menu title row in menu config line %d\n", fin_line);
443 return -1;
444 }
445 p = q;
446 while (isdigit(*q))
447 {
448 q++;
449 }
450 if (*q != '\0')
451 {
452 log_error("Error menu title row in menu config line %d\n", fin_line);
453 return -1;
454 }
455 p_menu->title.row = (int16_t)atoi(p);
456
457 // Menu title col
458 q = strtok_r(NULL, MENU_CONF_DELIM_WITH_SPACE, &saveptr);
459 if (q == NULL)
460 {
461 log_error("Error menu title col in menu config line %d\n", fin_line);
462 return -1;
463 }
464 p = q;
465 while (isdigit(*q))
466 {
467 q++;
468 }
469 if (*q != '\0')
470 {
471 log_error("Error menu title col in menu config line %d\n", fin_line);
472 return -1;
473 }
474 p_menu->title.col = (int16_t)atoi(p);
475
476 // Menu title text
477 q = strtok_r(NULL, MENU_CONF_DELIM_WITHOUT_SPACE, &saveptr);
478 if (q == NULL || (q = strchr(q, '\"')) == NULL)
479 {
480 log_error("Error menu title text in menu config line %d\n", fin_line);
481 return -1;
482 }
483 q++;
484 p = q;
485 while (*q != '\0' && *q != '\"')
486 {
487 if (*q == '\\')
488 {
489 r = q;
490 while (*r != '\0')
491 {
492 *r = *(r + 1);
493 r++;
494 }
495 }
496 q++;
497 }
498 if (*q != '\"')
499 {
500 log_error("Error menu title text in menu config line %d\n", fin_line);
501 return -1;
502 }
503 *q = '\0';
504
505 if (q - p > sizeof(p_menu->title.text) - 1)
506 {
507 log_error("Too longer menu title text in menu config line %d\n", fin_line);
508 return -1;
509 }
510 strncpy(p_menu->title.text, p, sizeof(p_menu->title.text) - 1);
511 p_menu->title.text[sizeof(p_menu->title.text) - 1] = '\0';
512
513 // Check syntax
514 q = strtok_r(q + 1, MENU_CONF_DELIM_WITH_SPACE, &saveptr);
515 if (q != NULL)
516 {
517 log_error("Unknown extra content in menu config line %d\n", fin_line);
518 return -1;
519 }
520 }
521 else if (strcmp(p, "screen") == 0)
522 {
523 p_menu->screen_show = 1;
524
525 // Menu screen row
526 q = strtok_r(NULL, MENU_CONF_DELIM_WITH_SPACE, &saveptr);
527 if (q == NULL)
528 {
529 log_error("Error menu screen row in menu config line %d\n", fin_line);
530 return -1;
531 }
532 p = q;
533 while (isdigit(*q))
534 {
535 q++;
536 }
537 if (*q != '\0')
538 {
539 log_error("Error menu screen row in menu config line %d\n", fin_line);
540 return -1;
541 }
542 p_menu->screen_row = (int16_t)atoi(p);
543
544 // Menu screen col
545 q = strtok_r(NULL, MENU_CONF_DELIM_WITH_SPACE, &saveptr);
546 if (q == NULL)
547 {
548 log_error("Error menu screen col in menu config line %d\n", fin_line);
549 return -1;
550 }
551 p = q;
552 while (isdigit(*q))
553 {
554 q++;
555 }
556 if (*q != '\0')
557 {
558 log_error("Error menu screen col in menu config line %d\n", fin_line);
559 return -1;
560 }
561 p_menu->screen_col = (int16_t)atoi(p);
562
563 // Menu screen name
564 q = strtok_r(NULL, MENU_CONF_DELIM_WITH_SPACE, &saveptr);
565 if (q == NULL)
566 {
567 log_error("Error menu screen name in menu config line %d\n", fin_line);
568 return -1;
569 }
570 p = q;
571 while (isalnum(*q) || *q == '_' || *q == '-')
572 {
573 q++;
574 }
575 if (*q != '\0')
576 {
577 log_error("Error menu screen name in menu config line %d\n", fin_line);
578 return -1;
579 }
580 strncpy(p_menu->screen_name, p, sizeof(p_menu->screen_name) - 1);
581 p_menu->screen_name[sizeof(p_menu->screen_name) - 1] = '\0';
582
583 // Check syntax
584 q = strtok_r(NULL, MENU_CONF_DELIM_WITH_SPACE, &saveptr);
585 if (q != NULL)
586 {
587 log_error("Unknown extra content in menu config line %d\n", fin_line);
588 return -1;
589 }
590 }
591 else if (strcmp(p, "page") == 0)
592 {
593 // Menu page row
594 q = strtok_r(NULL, MENU_CONF_DELIM_WITH_SPACE, &saveptr);
595 if (q == NULL)
596 {
597 log_error("Error menu page row in menu config line %d\n", fin_line);
598 return -1;
599 }
600 p = q;
601 while (isdigit(*q))
602 {
603 q++;
604 }
605 if (*q != '\0')
606 {
607 log_error("Error menu page row in menu config line %d\n", fin_line);
608 return -1;
609 }
610 p_menu->page_row = (int16_t)atoi(p);
611
612 // Menu page col
613 q = strtok_r(NULL, MENU_CONF_DELIM_WITH_SPACE, &saveptr);
614 if (q == NULL)
615 {
616 log_error("Error menu page col in menu config line %d\n", fin_line);
617 return -1;
618 }
619 p = q;
620 while (isdigit(*q))
621 {
622 q++;
623 }
624 if (*q != '\0')
625 {
626 log_error("Error menu page col in menu config line %d\n", fin_line);
627 return -1;
628 }
629 p_menu->page_col = (int16_t)atoi(p);
630
631 // Menu page item limit
632 q = strtok_r(NULL, MENU_CONF_DELIM_WITH_SPACE, &saveptr);
633 if (q == NULL)
634 {
635 log_error("Error menu page item limit in menu config line %d\n", fin_line);
636 return -1;
637 }
638 p = q;
639 while (isdigit(*q))
640 {
641 q++;
642 }
643 if (*q != '\0')
644 {
645 log_error("Error menu page item limit in menu config line %d\n", fin_line);
646 return -1;
647 }
648 p_menu->page_item_limit = (int16_t)atoi(p);
649
650 // Check syntax
651 q = strtok_r(NULL, MENU_CONF_DELIM_WITH_SPACE, &saveptr);
652 if (q != NULL)
653 {
654 log_error("Unknown extra content in menu config line %d\n", fin_line);
655 return -1;
656 }
657 }
658 else if (strcmp(p, "use_filter") == 0)
659 {
660 p_menu->use_filter = 1;
661
662 // Check syntax
663 q = strtok_r(NULL, MENU_CONF_DELIM_WITH_SPACE, &saveptr);
664 if (q != NULL)
665 {
666 log_error("Unknown extra content in menu config line %d\n", fin_line);
667 return -1;
668 }
669 }
670 }
671 }
672 else // BEGIN of menu screen
673 {
674 if (p_menu_set->menu_item_count >= MAX_MENUS)
675 {
676 log_error("Menu screen count (%d) exceed limit (%d)\n", p_menu_set->menu_screen_count, MAX_MENUS);
677 return -3;
678 }
679 screen_id = (MENU_SCREEN_ID)p_menu_set->menu_screen_count;
680 p_menu_set->menu_screen_count++;
681
682 p_screen = get_menu_screen_by_id(p_menu_set, screen_id);
683
684 q = p;
685 while (isalnum(*q) || *q == '_' || *q == '-')
686 {
687 q++;
688 }
689 if (*q != '\0')
690 {
691 log_error("Error menu screen name in menu config line %d\n", fin_line);
692 return -1;
693 }
694 strncpy(p_screen->name, p, sizeof(p_screen->name) - 1);
695 p_screen->name[sizeof(p_screen->name) - 1] = '\0';
696
697 if (trie_dict_set(p_menu_set->p_menu_screen_dict, p_screen->name, (int64_t)screen_id) != 1)
698 {
699 log_error("Error set menu screen dict [%s]\n", p_screen->name);
700 }
701
702 // Check syntax
703 q = strtok_r(NULL, MENU_CONF_DELIM_WITH_SPACE, &saveptr);
704 if (q != NULL)
705 {
706 log_error("Unknown extra content in menu config line %d\n", fin_line);
707 return -1;
708 }
709
710 p_screen->buf_offset = p_menu_set->p_menu_screen_buf_free - p_menu_set->p_menu_screen_buf;
711 p_screen->buf_length = -1;
712
713 // safety appending boundary
714 q = p_menu_set->p_menu_screen_buf + MAX_MENU_SCR_BUF_LENGTH * MAX_MENUS - 1;
715
716 while (fgets(buffer, sizeof(buffer), fin))
717 {
718 fin_line++;
719
720 strncpy(temp, buffer, sizeof(temp) - 1); // Duplicate line for strtok_r
721 temp[sizeof(temp) - 1] = '\0';
722 p = strtok_r(temp, MENU_CONF_DELIM_WITH_SPACE, &saveptr);
723 if (p != NULL && *p == '%') // END of menu screen
724 {
725 if (p_menu_set->p_menu_screen_buf_free + 1 > q)
726 {
727 log_error("Menu screen buffer depleted (%p + 1 > %p)\n", p_menu_set->p_menu_screen_buf_free, q);
728 return -3;
729 }
730
731 *(p_menu_set->p_menu_screen_buf_free) = '\0';
732 p_menu_set->p_menu_screen_buf_free++;
733 p_screen->buf_length = p_menu_set->p_menu_screen_buf_free - p_menu_set->p_menu_screen_buf - p_screen->buf_offset;
734 break;
735 }
736
737 // Clear line
738 if (p_menu_set->p_menu_screen_buf_free + strlen(CTRL_SEQ_CLR_LINE) > q)
739 {
740 log_error("Menu screen buffer depleted (%p + %d > %p)\n", p_menu_set->p_menu_screen_buf_free, q, strlen(CTRL_SEQ_CLR_LINE));
741 return -3;
742 }
743 p_menu_set->p_menu_screen_buf_free = stpcpy(p_menu_set->p_menu_screen_buf_free, CTRL_SEQ_CLR_LINE);
744
745 p = buffer;
746 while (*p != '\0')
747 {
748 if (p_menu_set->p_menu_screen_buf_free + 2 > q)
749 {
750 log_error("Menu screen buffer depleted (%p + 2 > %p)\n", p_menu_set->p_menu_screen_buf_free, q);
751 return -3;
752 }
753
754 if (*p == '\n' && p > buffer && *(p - 1) != '\r')
755 {
756 *(p_menu_set->p_menu_screen_buf_free) = '\r';
757 p_menu_set->p_menu_screen_buf_free++;
758 }
759
760 *(p_menu_set->p_menu_screen_buf_free) = *p;
761 p++;
762 p_menu_set->p_menu_screen_buf_free++;
763 }
764 }
765
766 if (p_screen->buf_length == -1)
767 {
768 log_error("End of menu screen [%s] not found\n", p_screen->name);
769 }
770 }
771 }
772 else // Invalid prefix
773 {
774 log_error("Error in menu config line %d\n", fin_line);
775 return -1;
776 }
777 }
778 fclose(fin);
779
780 for (menu_id = 0; menu_id < p_menu_set->menu_count; menu_id++)
781 {
782 p_menu = get_menu_by_id(p_menu_set, menu_id);
783
784 if (trie_dict_get(p_menu_set->p_menu_screen_dict, p_menu->screen_name, (int64_t *)(&(p_menu->screen_id))) != 1)
785 {
786 log_error("Undefined menu screen [%s]\n", p);
787 return -1;
788 }
789
790 // Set menu->filter_handler of each menu pointing to filter
791 if (p_menu->use_filter == 1)
792 {
793 if ((p_menu->filter_handler = get_cmd_handler(p_menu->name)) == NULL)
794 {
795 log_error("Undefined menu filter handler [%s]\n", p_menu->name);
796 return -1;
797 }
798 }
799 }
800
801 for (menu_item_id = 0; menu_item_id < p_menu_set->menu_item_count; menu_item_id++)
802 {
803 p_menu_item = get_menu_item_by_id(p_menu_set, menu_item_id);
804
805 // Set menu_item->action_cmd_handler of each menu item pointing to bbs_cmd
806 if (p_menu_item->submenu == 0)
807 {
808 if ((p_menu_item->action_cmd_handler = get_cmd_handler(p_menu_item->action)) == NULL)
809 {
810 log_error("Undefined menu action cmd handler [%s]\n", p_menu_item->action);
811 return -1;
812 }
813 }
814 // Set menu_item->action_menu_id of each menu item pointing to a submenu to the menu_id of the corresponding submenu
815 else if (strcmp(p_menu_item->action, "..") != 0)
816 {
817 if (trie_dict_get(p_menu_set->p_menu_name_dict, p_menu_item->action, (int64_t *)&menu_id) != 1)
818 {
819 log_error("Undefined sub menu id [%s]\n", p_menu_item->action);
820 return -1;
821 }
822 p_menu_item->action_menu_id = menu_id;
823 }
824 }
825
826 // Save status varaibles into reserved memory area
827 *((int16_t *)p_menu_set->p_reserved) = p_menu_set->menu_count;
828 *(((int16_t *)p_menu_set->p_reserved) + 1) = p_menu_set->menu_item_count;
829 *(((int16_t *)p_menu_set->p_reserved) + 2) = p_menu_set->menu_screen_count;
830
831 return 0;
832 }
833
834 int display_menu_cursor(MENU_SET *p_menu_set, int show)
835 {
836 MENU_ID menu_id;
837 MENU_ITEM_ID menu_item_id;
838 MENU *p_menu;
839 MENU_ITEM *p_menu_item;
840 int16_t menu_item_pos;
841
842 menu_id = p_menu_set->menu_id_path[p_menu_set->choose_step];
843 p_menu = get_menu_by_id(p_menu_set, menu_id);
844 if (p_menu == NULL)
845 {
846 log_error("get_menu_by_id(%d) return NULL pointer\n", menu_id);
847 return -1;
848 }
849
850 menu_item_pos = p_menu_set->menu_item_pos[p_menu_set->choose_step];
851 menu_item_id = p_menu->items[menu_item_pos];
852 p_menu_item = get_menu_item_by_id(p_menu_set, menu_item_id);
853 if (p_menu_item == NULL)
854 {
855 log_error("get_menu_item_by_id(%d) return NULL pointer\n", menu_item_id);
856 return -1;
857 }
858
859 moveto(p_menu_set->menu_item_r_row[menu_item_pos], p_menu_set->menu_item_r_col[menu_item_pos] - 2);
860 outc(show ? '>' : ' ');
861
862 return 0;
863 }
864
865 static int display_menu_current_page(MENU_SET *p_menu_set)
866 {
867 int16_t row = 0;
868 int16_t col = 0;
869 MENU_ID menu_id;
870 MENU_ITEM_ID menu_item_id;
871 MENU *p_menu;
872 MENU_ITEM *p_menu_item;
873 int16_t menu_item_pos;
874 int16_t page_id = 0;
875 MENU_SCREEN *p_menu_screen;
876
877 menu_id = p_menu_set->menu_id_path[p_menu_set->choose_step];
878 p_menu = get_menu_by_id(p_menu_set, menu_id);
879 if (p_menu == NULL)
880 {
881 log_error("get_menu_by_id(%d) return NULL pointer\n", menu_id);
882 return -1;
883 }
884
885 clrline(p_menu->page_row, p_menu->page_row + p_menu->page_item_limit - 1);
886
887 if (p_menu->title.show)
888 {
889 if (p_menu->title.row == 0 && p_menu->title.col == 0)
890 {
891 show_top(p_menu->title.text, BBS_name, "");
892 }
893 else
894 {
895 moveto(p_menu->title.row, p_menu->title.col);
896 prints("%s", p_menu->title.text);
897 }
898 }
899
900 if (p_menu->screen_show)
901 {
902 p_menu_screen = get_menu_screen_by_id(p_menu_set, p_menu->screen_id);
903 if (p_menu_screen == NULL)
904 {
905 log_error("get_menu_screen_by_id(%d) return NULL pointer\n", p_menu->screen_id);
906 return -1;
907 }
908
909 row = p_menu->screen_row;
910 col = p_menu->screen_col;
911
912 moveto(row, col);
913 prints("%s", p_menu_set->p_menu_screen_buf + p_menu_screen->buf_offset);
914 }
915
916 menu_item_pos = p_menu_set->menu_item_pos[p_menu_set->choose_step];
917 page_id = p_menu_set->menu_item_page_id[menu_item_pos];
918
919 while (menu_item_pos >= 0)
920 {
921 if (p_menu_set->menu_item_page_id[menu_item_pos] != page_id)
922 {
923 menu_item_pos++;
924 break;
925 }
926
927 if (menu_item_pos == 0)
928 {
929 break;
930 }
931
932 menu_item_pos--;
933 }
934
935 for (; menu_item_pos < p_menu->item_count; menu_item_pos++)
936 {
937 if (p_menu_set->menu_item_page_id[menu_item_pos] != page_id)
938 {
939 break;
940 }
941
942 menu_item_id = p_menu->items[menu_item_pos];
943 p_menu_item = get_menu_item_by_id(p_menu_set, menu_item_id);
944
945 if (p_menu_set->menu_item_display[menu_item_pos] == 0)
946 {
947 continue;
948 }
949
950 row = p_menu_set->menu_item_r_row[menu_item_pos];
951 col = p_menu_set->menu_item_r_col[menu_item_pos];
952
953 moveto(row, col);
954 prints("%s", p_menu_item->text);
955 }
956
957 return 0;
958 }
959
960 int display_menu(MENU_SET *p_menu_set)
961 {
962 int16_t row = 0;
963 int16_t col = 0;
964 int menu_selectable = 0;
965 MENU_ID menu_id;
966 MENU_ITEM_ID menu_item_id;
967 MENU *p_menu;
968 MENU_ITEM *p_menu_item;
969 int16_t menu_item_pos;
970 int16_t page_id = 0;
971 int page_item_count = 0;
972
973 menu_id = p_menu_set->menu_id_path[p_menu_set->choose_step];
974 p_menu = get_menu_by_id(p_menu_set, menu_id);
975 if (p_menu == NULL)
976 {
977 log_error("get_menu_by_id(%d) return NULL pointer\n", menu_id);
978 if (p_menu_set->choose_step > 0)
979 {
980 p_menu_set->choose_step--;
981 return REDRAW;
982 }
983 return EXITBBS;
984 }
985
986 menu_item_pos = p_menu_set->menu_item_pos[p_menu_set->choose_step];
987 menu_item_id = p_menu->items[menu_item_pos];
988 p_menu_item = get_menu_item_by_id(p_menu_set, menu_item_id);
989 if (p_menu_item == NULL)
990 {
991 log_error("get_menu_item_by_id(%d) return NULL pointer\n", menu_item_id);
992 menu_item_pos = 0;
993 }
994
995 if (menu_item_pos > 0 &&
996 !(p_menu->use_filter ? (p_menu->filter_handler((void *)p_menu_item) == 0)
997 : (checkpriv(&BBS_priv, 0, p_menu_item->priv) == 0 ||
998 checklevel2(&BBS_priv, p_menu_item->level) == 0)))
999 {
1000 menu_selectable = 1;
1001 }
1002
1003 for (menu_item_pos = 0; menu_item_pos < p_menu->item_count; menu_item_pos++)
1004 {
1005 menu_item_id = p_menu->items[menu_item_pos];
1006 p_menu_item = get_menu_item_by_id(p_menu_set, menu_item_id);
1007
1008 if (p_menu_item->row != 0)
1009 {
1010 row = p_menu_item->row;
1011 }
1012 if (p_menu_item->col != 0)
1013 {
1014 col = p_menu_item->col;
1015 }
1016
1017 p_menu_set->menu_item_page_id[menu_item_pos] = page_id;
1018
1019 if (p_menu->use_filter ? (p_menu->filter_handler((void *)p_menu_item) == 0)
1020 : (checkpriv(&BBS_priv, 0, p_menu_item->priv) == 0 ||
1021 checklevel2(&BBS_priv, p_menu_item->level) == 0))
1022 {
1023 p_menu_set->menu_item_display[menu_item_pos] = 0;
1024 p_menu_set->menu_item_r_row[menu_item_pos] = 0;
1025 p_menu_set->menu_item_r_col[menu_item_pos] = 0;
1026 }
1027 else
1028 {
1029 p_menu_set->menu_item_display[menu_item_pos] = 1;
1030
1031 if (!menu_selectable)
1032 {
1033 p_menu_set->menu_item_pos[p_menu_set->choose_step] = menu_item_pos;
1034 menu_selectable = 1;
1035 }
1036
1037 p_menu_set->menu_item_r_row[menu_item_pos] = row;
1038 p_menu_set->menu_item_r_col[menu_item_pos] = col;
1039
1040 row++;
1041
1042 page_item_count++;
1043 if (p_menu->page_item_limit > 0 && page_item_count >= p_menu->page_item_limit)
1044 {
1045 page_id++;
1046 page_item_count = 0;
1047 row = p_menu->page_row;
1048 col = p_menu->page_col;
1049 }
1050 }
1051 }
1052
1053 if (!menu_selectable)
1054 {
1055 log_error("No selectable menu item in current menu (%s)\n", p_menu->name);
1056 return -1;
1057 }
1058
1059 if (display_menu_current_page(p_menu_set) != 0)
1060 {
1061 return -1;
1062 }
1063
1064 display_menu_cursor(p_menu_set, 1);
1065
1066 return 0;
1067 }
1068
1069 int menu_control(MENU_SET *p_menu_set, int key)
1070 {
1071 MENU_ID menu_id;
1072 MENU_ITEM_ID menu_item_id;
1073 MENU *p_menu;
1074 MENU_ITEM *p_menu_item;
1075 int16_t menu_item_pos;
1076 int16_t page_id;
1077 int require_page_change = 0;
1078
1079 if (p_menu_set->menu_count == 0)
1080 {
1081 log_error("Empty menu set\n");
1082 return EXITBBS;
1083 }
1084
1085 menu_id = p_menu_set->menu_id_path[p_menu_set->choose_step];
1086 p_menu = get_menu_by_id(p_menu_set, menu_id);
1087 if (p_menu == NULL)
1088 {
1089 log_error("get_menu_by_id(%d) return NULL pointer\n", menu_id);
1090 if (p_menu_set->choose_step > 0)
1091 {
1092 p_menu_set->choose_step--;
1093 return REDRAW;
1094 }
1095 return EXITBBS;
1096 }
1097
1098 if (p_menu->item_count == 0)
1099 {
1100 log_error("Empty menu (%s)\n", p_menu->name);
1101 if (p_menu_set->choose_step > 0)
1102 {
1103 p_menu_set->choose_step--;
1104 return REDRAW;
1105 }
1106 return EXITBBS;
1107 }
1108
1109 menu_item_pos = p_menu_set->menu_item_pos[p_menu_set->choose_step];
1110 page_id = p_menu_set->menu_item_page_id[menu_item_pos];
1111
1112 menu_item_id = p_menu->items[menu_item_pos];
1113 p_menu_item = get_menu_item_by_id(p_menu_set, menu_item_id);
1114 if (p_menu_item == NULL)
1115 {
1116 log_error("get_menu_item_by_id(%d) return NULL pointer\n", menu_item_id);
1117 p_menu_set->menu_item_pos[p_menu_set->choose_step] = 0;
1118 return REDRAW;
1119 }
1120
1121 switch (key)
1122 {
1123 case CR:
1124 igetch_reset();
1125 case KEY_RIGHT:
1126 if (p_menu_item->submenu)
1127 {
1128 if (strcmp(p_menu_item->action, "..") == 0)
1129 {
1130 return menu_control(p_menu_set, KEY_LEFT);
1131 }
1132 p_menu_set->choose_step++;
1133 p_menu_set->menu_id_path[p_menu_set->choose_step] = p_menu_item->action_menu_id;
1134 p_menu_set->menu_item_pos[p_menu_set->choose_step] = 0;
1135
1136 if (display_menu(p_menu_set) != 0)
1137 {
1138 return menu_control(p_menu_set, KEY_LEFT);
1139 }
1140 }
1141 else
1142 {
1143 return ((*(p_menu_item->action_cmd_handler))((void *)(p_menu_item->name)));
1144 }
1145 break;
1146 case KEY_ESC:
1147 case KEY_LEFT:
1148 if (p_menu_set->choose_step > 0)
1149 {
1150 p_menu_set->choose_step--;
1151 if (p_menu_set->choose_step == 0)
1152 {
1153 return REDRAW;
1154 }
1155 if (display_menu(p_menu_set) != 0)
1156 {
1157 return menu_control(p_menu_set, KEY_LEFT);
1158 }
1159 }
1160 else
1161 {
1162 display_menu_cursor(p_menu_set, 0);
1163 menu_item_pos = p_menu->item_count - 1;
1164 while (menu_item_pos >= 0)
1165 {
1166 menu_item_id = p_menu->items[menu_item_pos];
1167 p_menu_item = get_menu_item_by_id(p_menu_set, menu_item_id);
1168 if (p_menu_item == NULL)
1169 {
1170 log_error("get_menu_item_by_id(%d) return NULL pointer\n", menu_item_id);
1171 return -1;
1172 }
1173
1174 if (!p_menu_set->menu_item_display[menu_item_pos] || p_menu_item->priv != 0 || p_menu_item->level != 0)
1175 {
1176 menu_item_pos--;
1177 }
1178 else
1179 {
1180 break;
1181 }
1182 }
1183 p_menu_set->menu_item_pos[p_menu_set->choose_step] = menu_item_pos;
1184 display_menu_cursor(p_menu_set, 1);
1185 }
1186 break;
1187 case KEY_PGUP:
1188 require_page_change = 1;
1189 case KEY_UP:
1190 display_menu_cursor(p_menu_set, 0);
1191 do
1192 {
1193 menu_item_pos--;
1194 if (menu_item_pos < 0)
1195 {
1196 menu_item_pos = p_menu->item_count - 1;
1197 require_page_change = 0;
1198 }
1199 menu_item_id = p_menu->items[menu_item_pos];
1200 p_menu_item = get_menu_item_by_id(p_menu_set, menu_item_id);
1201 if (p_menu_item == NULL)
1202 {
1203 log_error("get_menu_item_by_id(%d) return NULL pointer\n", menu_item_id);
1204 return -1;
1205 }
1206 if (require_page_change && p_menu_set->menu_item_page_id[menu_item_pos] != page_id)
1207 {
1208 require_page_change = 0;
1209 }
1210 } while (require_page_change || !p_menu_set->menu_item_display[menu_item_pos]);
1211 p_menu_set->menu_item_pos[p_menu_set->choose_step] = menu_item_pos;
1212 if (p_menu_set->menu_item_page_id[menu_item_pos] != page_id)
1213 {
1214 display_menu_current_page(p_menu_set);
1215 }
1216 display_menu_cursor(p_menu_set, 1);
1217 break;
1218 case KEY_PGDN:
1219 require_page_change = 1;
1220 case KEY_DOWN:
1221 display_menu_cursor(p_menu_set, 0);
1222 do
1223 {
1224 menu_item_pos++;
1225 if (menu_item_pos >= p_menu->item_count)
1226 {
1227 menu_item_pos = 0;
1228 require_page_change = 0;
1229 }
1230 menu_item_id = p_menu->items[menu_item_pos];
1231 p_menu_item = get_menu_item_by_id(p_menu_set, menu_item_id);
1232 if (p_menu_item == NULL)
1233 {
1234 log_error("get_menu_item_by_id(%d) return NULL pointer\n", menu_item_id);
1235 return -1;
1236 }
1237 if (require_page_change && p_menu_set->menu_item_page_id[menu_item_pos] != page_id)
1238 {
1239 require_page_change = 0;
1240 }
1241 } while (require_page_change || !p_menu_set->menu_item_display[menu_item_pos]);
1242 p_menu_set->menu_item_pos[p_menu_set->choose_step] = menu_item_pos;
1243 if (p_menu_set->menu_item_page_id[menu_item_pos] != page_id)
1244 {
1245 display_menu_current_page(p_menu_set);
1246 }
1247 display_menu_cursor(p_menu_set, 1);
1248 break;
1249 case KEY_HOME:
1250 display_menu_cursor(p_menu_set, 0);
1251 menu_item_pos = 0;
1252 while (menu_item_pos < p_menu->item_count - 1)
1253 {
1254 menu_item_id = p_menu->items[menu_item_pos];
1255 p_menu_item = get_menu_item_by_id(p_menu_set, menu_item_id);
1256 if (p_menu_item == NULL)
1257 {
1258 log_error("get_menu_item_by_id(%d) return NULL pointer\n", menu_item_id);
1259 return -1;
1260 }
1261
1262 if (p_menu_set->menu_item_display[menu_item_pos])
1263 {
1264 break;
1265 }
1266
1267 menu_item_pos++;
1268 }
1269 p_menu_set->menu_item_pos[p_menu_set->choose_step] = menu_item_pos;
1270 if (p_menu_set->menu_item_page_id[menu_item_pos] != page_id)
1271 {
1272 display_menu_current_page(p_menu_set);
1273 }
1274 display_menu_cursor(p_menu_set, 1);
1275 break;
1276 case KEY_END:
1277 display_menu_cursor(p_menu_set, 0);
1278 menu_item_pos = p_menu->item_count - 1;
1279 while (menu_item_pos > 0)
1280 {
1281 menu_item_id = p_menu->items[menu_item_pos];
1282 p_menu_item = get_menu_item_by_id(p_menu_set, menu_item_id);
1283 if (p_menu_item == NULL)
1284 {
1285 log_error("get_menu_item_by_id(%d) return NULL pointer\n", menu_item_id);
1286 return -1;
1287 }
1288
1289 if (p_menu_set->menu_item_display[menu_item_pos])
1290 {
1291 break;
1292 }
1293
1294 menu_item_pos--;
1295 }
1296 p_menu_set->menu_item_pos[p_menu_set->choose_step] = menu_item_pos;
1297 if (p_menu_set->menu_item_page_id[menu_item_pos] != page_id)
1298 {
1299 display_menu_current_page(p_menu_set);
1300 }
1301 display_menu_cursor(p_menu_set, 1);
1302 break;
1303 default:
1304 if (isalnum(key))
1305 {
1306 for (menu_item_pos = 0; menu_item_pos < p_menu->item_count; menu_item_pos++)
1307 {
1308 menu_item_id = p_menu->items[menu_item_pos];
1309 p_menu_item = get_menu_item_by_id(p_menu_set, menu_item_id);
1310 if (p_menu_item == NULL)
1311 {
1312 log_error("get_menu_item_by_id(%d) return NULL pointer\n", menu_item_id);
1313 return -1;
1314 }
1315
1316 if (toupper(key) == toupper(p_menu_item->name[0]) && p_menu_set->menu_item_display[menu_item_pos])
1317 {
1318 display_menu_cursor(p_menu_set, 0);
1319 p_menu_set->menu_item_pos[p_menu_set->choose_step] = menu_item_pos;
1320 if (p_menu_set->menu_item_page_id[menu_item_pos] != page_id)
1321 {
1322 display_menu_current_page(p_menu_set);
1323 }
1324 display_menu_cursor(p_menu_set, 1);
1325 break;
1326 }
1327 }
1328 }
1329 break;
1330 }
1331
1332 return NOREDRAW;
1333 }
1334
1335 int unload_menu(MENU_SET *p_menu_set)
1336 {
1337 int shmid;
1338
1339 if (p_menu_set == NULL)
1340 {
1341 return -1;
1342 }
1343
1344 if (p_menu_set->p_menu_name_dict != NULL)
1345 {
1346 trie_dict_destroy(p_menu_set->p_menu_name_dict);
1347 p_menu_set->p_menu_name_dict = NULL;
1348 }
1349
1350 if (p_menu_set->p_menu_screen_dict != NULL)
1351 {
1352 trie_dict_destroy(p_menu_set->p_menu_screen_dict);
1353 p_menu_set->p_menu_screen_dict = NULL;
1354 }
1355
1356 shmid = p_menu_set->shmid;
1357
1358 detach_menu_shm(p_menu_set);
1359
1360 if (shmctl(shmid, IPC_RMID, NULL) == -1)
1361 {
1362 log_error("shmctl(shmid=%d, IPC_RMID) error (%d)\n", shmid, errno);
1363 return -1;
1364 }
1365
1366 return 0;
1367 }
1368
1369 int set_menu_shm_readonly(MENU_SET *p_menu_set)
1370 {
1371 void *p_shm;
1372
1373 // Remap shared memory in read-only mode
1374 p_shm = shmat(p_menu_set->shmid, p_menu_set->p_reserved, SHM_RDONLY | SHM_REMAP);
1375 if (p_shm == (void *)-1)
1376 {
1377 log_error("shmat(menu_shm shmid = %d) error (%d)\n", p_menu_set->shmid, errno);
1378 return -1;
1379 }
1380
1381 p_menu_set->p_reserved = p_shm;
1382
1383 return 0;
1384 }
1385
1386 int detach_menu_shm(MENU_SET *p_menu_set)
1387 {
1388 p_menu_set->menu_count = 0;
1389 p_menu_set->menu_item_count = 0;
1390 p_menu_set->menu_screen_count = 0;
1391 p_menu_set->choose_step = 0;
1392
1393 p_menu_set->p_menu_pool = NULL;
1394 p_menu_set->p_menu_item_pool = NULL;
1395 p_menu_set->p_menu_screen_pool = NULL;
1396 p_menu_set->p_menu_screen_buf = NULL;
1397 p_menu_set->p_menu_screen_buf_free = NULL;
1398
1399 p_menu_set->p_menu_name_dict = NULL;
1400 p_menu_set->p_menu_screen_dict = NULL;
1401
1402 if (p_menu_set->p_reserved != NULL && shmdt(p_menu_set->p_reserved) == -1)
1403 {
1404 log_error("shmdt() error (%d)\n", errno);
1405 return -1;
1406 }
1407
1408 p_menu_set->p_reserved = NULL;
1409
1410 return 0;
1411 }

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