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

Contents of /lbbs/src/menu.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.43 - (show annotations)
Thu May 15 09:15:52 2025 UTC (10 months ago) by sysadm
Branch: MAIN
Changes since 1.42: +2 -0 lines
Content type: text/x-csrc
Add HOME/END handling for menu

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

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