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

Contents of /lbbs/src/menu.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.50 - (show annotations)
Mon May 19 02:18:39 2025 UTC (9 months, 4 weeks ago) by sysadm
Branch: MAIN
Changes since 1.49: +1 -1 lines
Content type: text/x-csrc
Fix bug

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

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