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

Contents of /lbbs/src/menu.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.52 - (show annotations)
Mon May 19 06:55:06 2025 UTC (9 months, 4 weeks ago) by sysadm
Branch: MAIN
Changes since 1.51: +4 -4 lines
Content type: text/x-csrc
Refine active board refresh

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 return -1;
955 }
956
957 menu_item_pos = p_menu_set->menu_item_pos[p_menu_set->choose_step];
958 menu_item_id = p_menu->items[menu_item_pos];
959 p_menu_item = get_menu_item_by_id(p_menu_set, menu_item_id);
960 if (p_menu_item == NULL)
961 {
962 log_error("get_menu_item_by_id(%d) return NULL pointer\n", menu_item_id);
963 return -1;
964 }
965
966 if (menu_item_pos > 0 &&
967 checkpriv(&BBS_priv, 0, p_menu_item->priv) != 0 &&
968 checklevel2(&BBS_priv, p_menu_item->level))
969 {
970 menu_selectable = 1;
971 }
972
973 for (menu_item_pos = 0; menu_item_pos < p_menu->item_count; menu_item_pos++)
974 {
975 menu_item_id = p_menu->items[menu_item_pos];
976 p_menu_item = get_menu_item_by_id(p_menu_set, menu_item_id);
977
978 if (p_menu_item->row != 0)
979 {
980 row = p_menu_item->row;
981 }
982 if (p_menu_item->col != 0)
983 {
984 col = p_menu_item->col;
985 }
986
987 p_menu_set->menu_item_page_id[menu_item_pos] = page_id;
988
989 if (checkpriv(&BBS_priv, 0, p_menu_item->priv) == 0 || checklevel2(&BBS_priv, p_menu_item->level) == 0)
990 {
991 p_menu_set->menu_item_display[menu_item_pos] = 0;
992 p_menu_set->menu_item_r_row[menu_item_pos] = 0;
993 p_menu_set->menu_item_r_col[menu_item_pos] = 0;
994 }
995 else
996 {
997 p_menu_set->menu_item_display[menu_item_pos] = 1;
998
999 if (!menu_selectable)
1000 {
1001 p_menu_set->menu_item_pos[p_menu_set->choose_step] = menu_item_pos;
1002 menu_selectable = 1;
1003 }
1004
1005 p_menu_set->menu_item_r_row[menu_item_pos] = row;
1006 p_menu_set->menu_item_r_col[menu_item_pos] = col;
1007
1008 row++;
1009
1010 page_item_count++;
1011 if (p_menu->page_item_limit > 0 && page_item_count >= p_menu->page_item_limit)
1012 {
1013 page_id++;
1014 page_item_count = 0;
1015 row = p_menu->page_row;
1016 col = p_menu->page_col;
1017 }
1018 }
1019 }
1020
1021 if (!menu_selectable)
1022 {
1023 return -1;
1024 }
1025
1026 display_menu_current_page(p_menu_set);
1027
1028 display_menu_cursor(p_menu_set, 1);
1029
1030 return 0;
1031 }
1032
1033 int menu_control(MENU_SET *p_menu_set, int key)
1034 {
1035 MENU_ID menu_id;
1036 MENU_ITEM_ID menu_item_id;
1037 MENU *p_menu;
1038 MENU_ITEM *p_menu_item;
1039 int16_t menu_item_pos;
1040 int16_t page_id;
1041 int require_page_change = 0;
1042
1043 if (p_menu_set->menu_count == 0)
1044 {
1045 return 0;
1046 }
1047
1048 menu_id = p_menu_set->menu_id_path[p_menu_set->choose_step];
1049 p_menu = get_menu_by_id(p_menu_set, menu_id);
1050 if (p_menu == NULL)
1051 {
1052 log_error("get_menu_by_id(%d) return NULL pointer\n", menu_id);
1053 return -1;
1054 }
1055
1056 if (p_menu->item_count == 0)
1057 {
1058 return 0;
1059 }
1060
1061 menu_item_pos = p_menu_set->menu_item_pos[p_menu_set->choose_step];
1062 page_id = p_menu_set->menu_item_page_id[menu_item_pos];
1063
1064 menu_item_id = p_menu->items[menu_item_pos];
1065 p_menu_item = get_menu_item_by_id(p_menu_set, menu_item_id);
1066 if (p_menu_item == NULL)
1067 {
1068 log_error("get_menu_item_by_id(%d) return NULL pointer\n", menu_item_id);
1069 return -1;
1070 }
1071
1072 switch (key)
1073 {
1074 case CR:
1075 igetch_reset();
1076 case KEY_RIGHT:
1077 if (p_menu_item->submenu)
1078 {
1079 if (strcmp(p_menu_item->action, "..") == 0)
1080 {
1081 return menu_control(p_menu_set, KEY_LEFT);
1082 }
1083 p_menu_set->choose_step++;
1084 p_menu_set->menu_id_path[p_menu_set->choose_step] = p_menu_item->action_menu_id;
1085 p_menu_set->menu_item_pos[p_menu_set->choose_step] = 0;
1086
1087 if (display_menu(p_menu_set) != 0)
1088 {
1089 return menu_control(p_menu_set, KEY_LEFT);
1090 }
1091 }
1092 else
1093 {
1094 return ((*(p_menu_item->action_cmd_handler))((void *)(p_menu_item->name)));
1095 }
1096 break;
1097 case KEY_LEFT:
1098 if (p_menu_set->choose_step > 0)
1099 {
1100 p_menu_set->choose_step--;
1101 if (p_menu_set->choose_step == 0)
1102 {
1103 return REDRAW;
1104 }
1105 if (display_menu(p_menu_set) != 0)
1106 {
1107 return menu_control(p_menu_set, KEY_LEFT);
1108 }
1109 }
1110 else
1111 {
1112 display_menu_cursor(p_menu_set, 0);
1113 menu_item_pos = p_menu->item_count - 1;
1114 while (menu_item_pos >= 0)
1115 {
1116 menu_item_id = p_menu->items[menu_item_pos];
1117 p_menu_item = get_menu_item_by_id(p_menu_set, menu_item_id);
1118 if (p_menu_item == NULL)
1119 {
1120 log_error("get_menu_item_by_id(%d) return NULL pointer\n", menu_item_id);
1121 return -1;
1122 }
1123
1124 if (!p_menu_set->menu_item_display[menu_item_pos] || p_menu_item->priv != 0 || p_menu_item->level != 0)
1125 {
1126 menu_item_pos--;
1127 }
1128 else
1129 {
1130 break;
1131 }
1132 }
1133 p_menu_set->menu_item_pos[p_menu_set->choose_step] = menu_item_pos;
1134 display_menu_cursor(p_menu_set, 1);
1135 }
1136 break;
1137 case KEY_PGUP:
1138 require_page_change = 1;
1139 case KEY_UP:
1140 display_menu_cursor(p_menu_set, 0);
1141 do
1142 {
1143 menu_item_pos--;
1144 if (menu_item_pos < 0)
1145 {
1146 menu_item_pos = p_menu->item_count - 1;
1147 require_page_change = 0;
1148 }
1149 menu_item_id = p_menu->items[menu_item_pos];
1150 p_menu_item = get_menu_item_by_id(p_menu_set, menu_item_id);
1151 if (p_menu_item == NULL)
1152 {
1153 log_error("get_menu_item_by_id(%d) return NULL pointer\n", menu_item_id);
1154 return -1;
1155 }
1156 if (require_page_change && p_menu_set->menu_item_page_id[menu_item_pos] != page_id)
1157 {
1158 require_page_change = 0;
1159 }
1160 } while (require_page_change || !p_menu_set->menu_item_display[menu_item_pos]);
1161 p_menu_set->menu_item_pos[p_menu_set->choose_step] = menu_item_pos;
1162 if (p_menu_set->menu_item_page_id[menu_item_pos] != page_id)
1163 {
1164 display_menu_current_page(p_menu_set);
1165 }
1166 display_menu_cursor(p_menu_set, 1);
1167 break;
1168 case KEY_PGDN:
1169 require_page_change = 1;
1170 case KEY_DOWN:
1171 display_menu_cursor(p_menu_set, 0);
1172 do
1173 {
1174 menu_item_pos++;
1175 if (menu_item_pos >= p_menu->item_count)
1176 {
1177 menu_item_pos = 0;
1178 require_page_change = 0;
1179 }
1180 menu_item_id = p_menu->items[menu_item_pos];
1181 p_menu_item = get_menu_item_by_id(p_menu_set, menu_item_id);
1182 if (p_menu_item == NULL)
1183 {
1184 log_error("get_menu_item_by_id(%d) return NULL pointer\n", menu_item_id);
1185 return -1;
1186 }
1187 if (require_page_change && p_menu_set->menu_item_page_id[menu_item_pos] != page_id)
1188 {
1189 require_page_change = 0;
1190 }
1191 } while (require_page_change || !p_menu_set->menu_item_display[menu_item_pos]);
1192 p_menu_set->menu_item_pos[p_menu_set->choose_step] = menu_item_pos;
1193 if (p_menu_set->menu_item_page_id[menu_item_pos] != page_id)
1194 {
1195 display_menu_current_page(p_menu_set);
1196 }
1197 display_menu_cursor(p_menu_set, 1);
1198 break;
1199 case KEY_HOME:
1200 display_menu_cursor(p_menu_set, 0);
1201 menu_item_pos = 0;
1202 while (menu_item_pos < p_menu->item_count - 1)
1203 {
1204 menu_item_id = p_menu->items[menu_item_pos];
1205 p_menu_item = get_menu_item_by_id(p_menu_set, menu_item_id);
1206 if (p_menu_item == NULL)
1207 {
1208 log_error("get_menu_item_by_id(%d) return NULL pointer\n", menu_item_id);
1209 return -1;
1210 }
1211
1212 if (p_menu_set->menu_item_display[menu_item_pos])
1213 {
1214 break;
1215 }
1216
1217 menu_item_pos++;
1218 }
1219 p_menu_set->menu_item_pos[p_menu_set->choose_step] = menu_item_pos;
1220 if (p_menu_set->menu_item_page_id[menu_item_pos] != page_id)
1221 {
1222 display_menu_current_page(p_menu_set);
1223 }
1224 display_menu_cursor(p_menu_set, 1);
1225 break;
1226 case KEY_END:
1227 display_menu_cursor(p_menu_set, 0);
1228 menu_item_pos = p_menu->item_count - 1;
1229 while (menu_item_pos > 0)
1230 {
1231 menu_item_id = p_menu->items[menu_item_pos];
1232 p_menu_item = get_menu_item_by_id(p_menu_set, menu_item_id);
1233 if (p_menu_item == NULL)
1234 {
1235 log_error("get_menu_item_by_id(%d) return NULL pointer\n", menu_item_id);
1236 return -1;
1237 }
1238
1239 if (p_menu_set->menu_item_display[menu_item_pos])
1240 {
1241 break;
1242 }
1243
1244 menu_item_pos--;
1245 }
1246 p_menu_set->menu_item_pos[p_menu_set->choose_step] = menu_item_pos;
1247 if (p_menu_set->menu_item_page_id[menu_item_pos] != page_id)
1248 {
1249 display_menu_current_page(p_menu_set);
1250 }
1251 display_menu_cursor(p_menu_set, 1);
1252 break;
1253 default:
1254 if (isalnum(key))
1255 {
1256 for (menu_item_pos = 0; menu_item_pos < p_menu->item_count; menu_item_pos++)
1257 {
1258 menu_item_id = p_menu->items[menu_item_pos];
1259 p_menu_item = get_menu_item_by_id(p_menu_set, menu_item_id);
1260 if (p_menu_item == NULL)
1261 {
1262 log_error("get_menu_item_by_id(%d) return NULL pointer\n", menu_item_id);
1263 return -1;
1264 }
1265
1266 if (toupper(key) == toupper(p_menu_item->name[0]) && p_menu_set->menu_item_display[menu_item_pos])
1267 {
1268 display_menu_cursor(p_menu_set, 0);
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 }
1277 }
1278 }
1279 break;
1280 }
1281
1282 return NOREDRAW;
1283 }
1284
1285 int unload_menu(MENU_SET *p_menu_set)
1286 {
1287 if (p_menu_set->p_menu_name_dict != NULL)
1288 {
1289 trie_dict_destroy(p_menu_set->p_menu_name_dict);
1290 p_menu_set->p_menu_name_dict = NULL;
1291 }
1292
1293 if (p_menu_set->p_menu_screen_dict != NULL)
1294 {
1295 trie_dict_destroy(p_menu_set->p_menu_screen_dict);
1296 p_menu_set->p_menu_screen_dict = NULL;
1297 }
1298
1299 unload_menu_shm(p_menu_set);
1300
1301 if (shmctl(p_menu_set->shmid, IPC_RMID, NULL) == -1)
1302 {
1303 log_error("shmctl(shmid=%d, IPC_RMID) error (%d)\n", p_menu_set->shmid, errno);
1304 return -1;
1305 }
1306
1307 return 0;
1308 }
1309
1310 int load_menu_shm(MENU_SET *p_menu_set)
1311 {
1312 // Mount shared memory
1313 if (p_menu_set->p_reserved == NULL)
1314 {
1315 p_menu_set->p_reserved = shmat(p_menu_set->shmid, NULL, SHM_RDONLY);
1316 if (p_menu_set->p_reserved == (void *)-1)
1317 {
1318 log_error("shmat() error (%d)\n", errno);
1319 return -1;
1320 }
1321 }
1322
1323 p_menu_set->p_menu_pool = p_menu_set->p_reserved + MENU_SET_RESERVED_LENGTH;
1324 p_menu_set->p_menu_item_pool = p_menu_set->p_menu_pool + sizeof(MENU) * MAX_MENUS;
1325 p_menu_set->p_menu_screen_pool = p_menu_set->p_menu_item_pool + sizeof(MENU_ITEM) * MAX_MENUITEMS;
1326 p_menu_set->p_menu_screen_buf = p_menu_set->p_menu_screen_pool + sizeof(MENU_SCREEN) * MAX_MENUS;
1327 p_menu_set->p_menu_screen_buf_free = p_menu_set->p_menu_screen_buf;
1328
1329 // Restore status varaibles into reserved memory area
1330 p_menu_set->menu_count = *((int16_t *)p_menu_set->p_reserved);
1331 p_menu_set->menu_item_count = *(((int16_t *)p_menu_set->p_reserved) + 1);
1332 p_menu_set->menu_screen_count = *(((int16_t *)p_menu_set->p_reserved) + 2);
1333
1334 p_menu_set->choose_step = 0;
1335 p_menu_set->menu_id_path[0] = 0;
1336
1337 p_menu_set->p_menu_name_dict = NULL;
1338 p_menu_set->p_menu_screen_dict = NULL;
1339
1340 return 0;
1341 }
1342
1343 int unload_menu_shm(MENU_SET *p_menu_set)
1344 {
1345 p_menu_set->menu_count = 0;
1346 p_menu_set->menu_item_count = 0;
1347 p_menu_set->menu_screen_count = 0;
1348 p_menu_set->choose_step = 0;
1349
1350 p_menu_set->p_menu_pool = NULL;
1351 p_menu_set->p_menu_item_pool = NULL;
1352 p_menu_set->p_menu_screen_pool = NULL;
1353 p_menu_set->p_menu_screen_buf = NULL;
1354 p_menu_set->p_menu_screen_buf_free = NULL;
1355
1356 if (p_menu_set->p_reserved != NULL && shmdt(p_menu_set->p_reserved) == -1)
1357 {
1358 log_error("shmdt() error (%d)\n", errno);
1359 return -1;
1360 }
1361 p_menu_set->p_reserved = NULL;
1362
1363 return 0;
1364 }

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