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

Contents of /lbbs/src/menu.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.57 - (show annotations)
Tue May 27 00:54:01 2025 UTC (9 months, 3 weeks ago) by sysadm
Branch: MAIN
Changes since 1.56: +7 -3 lines
Content type: text/x-csrc
Add attach/detach SHM in readonly mode for child process

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

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