AmendHub

Download

jcs

/

subtext

/

main_menu.c

 

(View History)

jcs   *: Add dynamically configured main menu Latest amendment: 281 on 2022-11-11

1 /*
2 * Copyright (c) 2022 joshua stein <jcs@jcs.org>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17 #include <stdio.h>
18 #include <string.h>
19
20 #include "subtext.h"
21 #include "db.h"
22 #include "main_menu.h"
23
24 struct main_menu_option *main_menu_options = NULL;
25
26 void
27 main_menu_init(void)
28 {
29 struct main_menu_option *opts = NULL, *old_opts = NULL;
30 size_t size;
31 char *menu = NULL;
32 bool ok = false;
33 Handle default_menu;
34
35 size = bile_read_alloc(db->bile, DB_TEXT_TYPE, DB_TEXT_MENU_OPTIONS_ID,
36 &menu);
37 if (size == 0 || menu == NULL) {
38 /* use default menu */
39 if (menu != NULL)
40 xfree(&menu);
41 default_menu = GetResource('TEXT', MENU_DEFAULTS_ID);
42 if (default_menu == NULL)
43 panic("Failed to find TEXT resource %d", MENU_DEFAULTS_ID);
44 size = GetHandleSize(default_menu);
45 menu = xmalloc(size, "menu copy");
46 HLock(default_menu);
47 memcpy(menu, *default_menu, size);
48 HUnlock(default_menu);
49 ReleaseResource(default_menu);
50
51 bile_write(db->bile, DB_TEXT_TYPE, DB_TEXT_MENU_OPTIONS_ID,
52 menu, size);
53 }
54
55 opts = main_menu_parse(menu, size);
56 free(&menu);
57 if (opts == NULL) {
58 main_menu_edit();
59 return;
60 }
61
62 /* swap this atomically */
63 old_opts = main_menu_options;
64 main_menu_options = opts;
65 if (old_opts != NULL)
66 xfree(&old_opts);
67 }
68
69 void
70 main_menu_edit(void)
71 {
72 view_editor_show(DB_TEXT_MENU_OPTIONS_ID, "Edit: Main Menu options");
73 }
74
75 struct main_menu_option *
76 main_menu_parse(char *opts, size_t len)
77 {
78 char *line, *action, *menu_key, *all_keys, *label;
79 size_t n, m, linelen, lastsep, linenum, ret_size;
80 short count, actionid, ret_count;
81 struct main_menu_option *ret = NULL;
82
83 #define LINESIZE 1024
84 line = xmalloc(LINESIZE, "parse_menu line");
85 action = xmalloc(LINESIZE, "parse_menu action");
86 menu_key = xmalloc(LINESIZE, "parse_menu menu_key");
87 all_keys = xmalloc(LINESIZE, "parse_menu all_keys");
88 label = xmalloc(LINESIZE, "parse_menu label");
89
90 ret_count = 0;
91 ret_size = 0;
92
93 for (n = 0, lastsep = 0, linenum = 0; n <= len; n++) {
94 if (!(n == len || opts[n] == '\r'))
95 continue;
96
97 linenum++;
98 linelen = MIN(n - lastsep, LINESIZE - 1);
99 if (n == len && linelen)
100 linelen--;
101 memcpy(line, opts + lastsep, linelen);
102 line[linelen] = '\0';
103 lastsep = n + 1;
104
105 if (line[0] == '\0' || line[0] == '#' || line[0] == '\r')
106 continue;
107
108 /* BOARD_SHOW_FIRST:B:Bb:Message Board */
109
110 /* sscanf won't match %[^:] for an empty section ("A::Aa:aa") */
111 action[0] = '\0';
112 for (m = 0; m < linelen; m++) {
113 if (line[m] == ':') {
114 memcpy(action, line, m);
115 action[m] = '\0';
116 linelen -= m + 1;
117 memmove(line, line + m + 1, linelen + 1);
118 break;
119 }
120 }
121 if (action[0] == '\0') {
122 warn("Error on line %ld: no action found", linenum);
123 ret_count = 0;
124 break;
125 }
126
127 actionid = ACTION_NONE;
128 if (strcmp(action, "BOARD_SHOW_FIRST") == 0)
129 actionid = ACTION_BOARD_SHOW_FIRST;
130 else if (strcmp(action, "BOARD_SHOW_1") == 0)
131 actionid = ACTION_BOARD_SHOW_1;
132 else if (strcmp(action, "BOARD_SHOW_2") == 0)
133 actionid = ACTION_BOARD_SHOW_2;
134 else if (strcmp(action, "BOARD_SHOW_3") == 0)
135 actionid = ACTION_BOARD_SHOW_3;
136 else if (strcmp(action, "BOARD_SHOW_4") == 0)
137 actionid = ACTION_BOARD_SHOW_4;
138 else if (strcmp(action, "BOARD_SHOW_5") == 0)
139 actionid = ACTION_BOARD_SHOW_5;
140 else if (strcmp(action, "BOARD_SHOW_6") == 0)
141 actionid = ACTION_BOARD_SHOW_6;
142 else if (strcmp(action, "BOARD_SHOW_7") == 0)
143 actionid = ACTION_BOARD_SHOW_7;
144 else if (strcmp(action, "BOARD_SHOW_8") == 0)
145 actionid = ACTION_BOARD_SHOW_8;
146 else if (strcmp(action, "BOARD_SHOW_9") == 0)
147 actionid = ACTION_BOARD_SHOW_9;
148 else if (strcmp(action, "BOARD_SHOW_10") == 0)
149 actionid = ACTION_BOARD_SHOW_10;
150 else if (strcmp(action, "CHAT") == 0)
151 actionid = ACTION_CHAT;
152 else if (strcmp(action, "FILES_MENU") == 0)
153 actionid = ACTION_FILES_MENU;
154 else if (strcmp(action, "GOODBYE") == 0)
155 actionid = ACTION_GOODBYE;
156 else if (strcmp(action, "RECENT_LOGINS") == 0)
157 actionid = ACTION_RECENT_LOGINS;
158 else if (strcmp(action, "MAIL_COMPOSE") == 0)
159 actionid = ACTION_MAIL_COMPOSE;
160 else if (strcmp(action, "MAIL_MENU") == 0)
161 actionid = ACTION_MAIL_MENU;
162 else if (strcmp(action, "MOTD") == 0)
163 actionid = ACTION_MOTD;
164 else if (strcmp(action, "PAGE_SEND_OR_ANSWER") == 0)
165 actionid = ACTION_PAGE_SEND_OR_ANSWER;
166 else if (strcmp(action, "SETTINGS_OR_SIGNUP") == 0)
167 actionid = ACTION_SETTINGS_OR_SIGNUP;
168 else if (strcmp(action, "SHOW_MENU") == 0)
169 actionid = ACTION_SHOW_MENU;
170 else if (strcmp(action, "SYSOP_MENU") == 0)
171 actionid = ACTION_SYSOP_MENU;
172 else if (strcmp(action, "WHOS_ONLINE") == 0)
173 actionid = ACTION_WHOS_ONLINE;
174 else {
175 warn("Error on line %ld: invalid action \"%s\"", linenum,
176 action);
177 ret_count = 0;
178 break;
179 }
180
181 menu_key[0] = '\0';
182 for (m = 0; m < linelen; m++) {
183 if (line[m] == ':') {
184 memcpy(menu_key, line, m);
185 menu_key[m] = '\0';
186 linelen -= m + 1;
187 memmove(line, line + m + 1, linelen + 1);
188 break;
189 }
190 }
191 /* menu_key can be 1 or 0 characters */
192 if (strlen(menu_key) > 1) {
193 warn("Error on line %ld: Menu Key can only be 1 character",
194 linenum);
195 ret_count = 0;
196 break;
197 }
198
199 all_keys[0] = '\0';
200 for (m = 0; m < linelen; m++) {
201 if (line[m] == ':') {
202 memcpy(all_keys, line, m);
203 all_keys[m] = '\0';
204 linelen -= m + 1;
205 memmove(line, line + m + 1, linelen + 1);
206 break;
207 }
208 }
209 if (all_keys[0] == '\0') {
210 warn("Error on line %ld: All Keys field cannot be empty",
211 linenum);
212 ret_count = 0;
213 break;
214 }
215
216 if (line[0] == '\0') {
217 warn("Error on line %ld: Label cannot be empty",
218 linenum);
219 ret_count = 0;
220 break;
221 }
222 strlcpy(label, line, LINESIZE);
223
224 EXPAND_TO_FIT(ret, ret_size,
225 sizeof(struct main_menu_option) * ret_count,
226 sizeof(struct main_menu_option), 4);
227
228 ret[ret_count].action = actionid;
229 ret[ret_count].menu_key = menu_key[0];
230 strlcpy(ret[ret_count].all_keys, all_keys,
231 sizeof(ret[ret_count].all_keys));
232 strlcpy(ret[ret_count].label, label, sizeof(ret[ret_count].label));
233 ret_count++;
234 }
235
236 xfree(&line);
237 xfree(&action);
238 xfree(&menu_key);
239 xfree(&all_keys);
240 xfree(&label);
241
242 if (ret_count == 0) {
243 xfree(&ret);
244 return NULL;
245 }
246
247 EXPAND_TO_FIT(ret, ret_size,
248 sizeof(struct main_menu_option) * ret_count,
249 sizeof(struct main_menu_option), 1);
250
251 ret[ret_count].action = ACTION_NONE;
252 ret[ret_count].menu_key = 0;
253 ret[ret_count].all_keys[0] = '\0';
254 ret[ret_count].label[0] = '\0';
255
256 return ret;
257 #undef LINESIZE
258 }