AmendHub

Download

jcs

/

subtext

/

main.c

 

(View History)

jcs   main: When MALLOC_DEBUG is defined, add a Debug menu to dump allocs Latest amendment: 588 on 2024-02-15

1 /*
2 * Copyright (c) 2020-2023 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 "binkp.h"
22 #include "console.h"
23 #include "db.h"
24 #include "focusable.h"
25 #include "logger.h"
26 #include "mail.h"
27 #include "main_menu.h"
28 #include "serial_local.h"
29 #include "session.h"
30 #include "settings.h"
31 #include "telnet.h"
32 #include "user.h"
33 #include "uthread.h"
34 #include "util.h"
35
36 MenuHandle apple_menu, file_menu;
37 short quitting = 0;
38 struct db *db = NULL;
39 bool blanker_on = false;
40 unsigned long blanker_last_blank = 0;
41 WindowPtr blanker_win = NULL;
42 struct uthread *periodic_job_thread = NULL;
43
44 #ifdef MALLOC_DEBUG
45 MenuHandle debug_menu;
46 #define DEBUG_MENU_DUMP_ID 999
47 #endif
48
49 bool handle_menu(long menu_id);
50 void handle_exit(void);
51 void blanker_idle(void);
52 void periodic_jobs(struct uthread *uthread, void *arg);
53
54 int
55 main(void)
56 {
57 Handle mbar;
58 EventRecord event;
59 WindowPtr event_win;
60 GrafPtr old_port;
61 AppFile finder_file;
62 struct focusable *found_focusable;
63 unsigned long zone_size, stack_size, heap_size;
64 short event_in, n, finder_action, finder_count;
65 char key, none = 0;
66 bool ignore = false;
67
68 uthread_init();
69
70 InitGraf(&thePort);
71 InitFonts();
72 FlushEvents(everyEvent, 0);
73 InitWindows();
74 InitMenus();
75 TEInit();
76 InitDialogs(0);
77 InitCursor();
78 MaxApplZone();
79
80 util_init();
81
82 if (!(mbar = GetNewMBar(MBAR_ID)))
83 panic("no mbar");
84 SetMenuBar(mbar);
85 if (!(apple_menu = GetMHandle(APPLE_MENU_ID)))
86 panic("no apple menu");
87 AddResMenu(apple_menu, 'DRVR');
88 if (!(file_menu = GetMHandle(FILE_MENU_ID)))
89 panic("no file menu");
90 #ifdef MALLOC_DEBUG
91 debug_menu = NewMenu(DEBUG_MENU_DUMP_ID, "\pDebug");
92 AppendMenu(debug_menu, "\pDump Allocations");
93 InsertMenu(debug_menu, 0);
94 #endif
95 DrawMenuBar();
96
97 /* see if we were started by double-clicking a .bbs file */
98 CountAppFiles(&finder_action, &finder_count);
99 if (finder_count) {
100 GetAppFiles(1, &finder_file);
101 ClrAppFiles(1);
102 finder_count = 0;
103 db = db_open(finder_file.fName, finder_file.vRefNum, false);
104 } else {
105 GetNextEvent(everyEvent, &event);
106 if (event.modifiers & cmdKey)
107 /* don't open last-opened database */
108 ignore = true;
109
110 db = db_open(*((Str255 *)&none), 0, ignore);
111 }
112
113 if (!db)
114 panic("Failed to open database");
115
116 _atexit(handle_exit);
117
118 logger_init();
119 logger_update_title();
120 if (db->config.syslog_ip)
121 syslog_init();
122
123 zone_size = (unsigned long)CurStackBase - (unsigned long)ApplZone;
124 stack_size = (unsigned long)CurStackBase - (unsigned long)ApplLimit;
125 heap_size = (unsigned long)ApplLimit - (unsigned long)ApplZone;
126 logger_printf("Initialized with %ldKB zone, %ldKB stack for "
127 "%d threads, %ldKB heap", zone_size / 1024L, stack_size / 1024L,
128 NUM_UTHREADS, heap_size / 1024L);
129
130 logger_printf("[db] Updating username cache");
131 user_cache_usernames();
132
133 if (db->config.ipdb_path[0])
134 db->ipdb = ipdb_open(db->config.ipdb_path);
135
136 db_cache_views(db);
137 db_cache_boards(db);
138 db_cache_folders(db);
139
140 binkp_init();
141 telnet_init();
142 serial_init();
143
144 blanker_last_blank = Time;
145
146 periodic_job_thread = uthread_add(periodic_jobs, NULL);
147
148 while (!quitting) {
149 telnet_idle();
150 serial_idle();
151 uthread_coordinate();
152
153 SystemTask();
154 if (!GetNextEvent(everyEvent, &event)) {
155 blanker_idle();
156 continue;
157 }
158
159 switch (event.what) {
160 case nullEvent:
161 for (n = 0; n < nfocusables; n++) {
162 if (focusables[n]->idle)
163 focusables[n]->idle(focusables[n], &event);
164 }
165 break;
166 case keyDown:
167 case autoKey:
168 if (blanker_on) {
169 blanker_unblank();
170 break;
171 }
172 key = event.message & charCodeMask;
173 if ((event.modifiers & cmdKey) != 0 &&
174 handle_menu(MenuKey(key)) == true)
175 break;
176 else if (nfocusables && focusables[0]->visible &&
177 focusables[0]->key_down)
178 focusables[0]->key_down(focusables[0], &event);
179 break;
180 case mouseDown:
181 event_in = FindWindow(event.where, &event_win);
182
183 switch (event_in) {
184 case inMenuBar:
185 handle_menu(MenuSelect(event.where));
186 break;
187 case inSysWindow:
188 SystemClick(&event, event_win);
189 break;
190 case inDrag:
191 SelectWindow(event_win);
192 DragWindow(event_win, event.where, &screenBits.bounds);
193 break;
194 case inGrow:
195 found_focusable = find_focusable(event_win);
196 if (event_win != FrontWindow()) {
197 if (found_focusable)
198 show_focusable(found_focusable);
199 }
200 if (found_focusable && found_focusable->resize)
201 found_focusable->resize(found_focusable, &event);
202 break;
203 case inGoAway:
204 if (TrackGoAway(event_win, event.where)) {
205 found_focusable = find_focusable(event_win);
206 if (found_focusable && found_focusable->close)
207 found_focusable->close(found_focusable, &event);
208 }
209 break;
210 case inContent:
211 if (blanker_on) {
212 blanker_unblank();
213 break;
214 }
215 found_focusable = find_focusable(event_win);
216 if (event_win != FrontWindow()) {
217 if (found_focusable)
218 show_focusable(found_focusable);
219 }
220 if (found_focusable && found_focusable->mouse_down)
221 found_focusable->mouse_down(found_focusable, &event);
222 break;
223 }
224 break;
225 case updateEvt:
226 event_win = (WindowPtr)event.message;
227
228 GetPort(&old_port);
229 SetPort(event_win);
230 BeginUpdate(event_win);
231
232 found_focusable = find_focusable(event_win);
233 if (found_focusable && found_focusable->update)
234 found_focusable->update(found_focusable, &event);
235
236 EndUpdate(event_win);
237 SetPort(old_port);
238 break;
239 case activateEvt:
240 break;
241 case app4Evt:
242 if (HiWord(event.message) & (1 << 8)) {
243 /* multifinder suspend/resume */
244 switch (event.message & (1 << 0)) {
245 case 0:
246 /* suspend */
247 for (n = 0; n < nfocusables; n++) {
248 if (focusables[n]->suspend)
249 focusables[n]->suspend(focusables[n], &event);
250 }
251 break;
252 case 1:
253 /* resume */
254 for (n = 0; n < nfocusables; n++) {
255 if (focusables[n]->resume)
256 focusables[n]->resume(focusables[n], &event);
257 }
258 break;
259 }
260 }
261 break;
262 }
263 }
264
265 return 0;
266 }
267
268 void
269 handle_exit(void)
270 {
271 short n;
272
273 blanker_unblank();
274
275 for (n = 0; n < nfocusables; n++) {
276 if (focusables[n]->atexit)
277 focusables[n]->atexit(focusables[n]);
278 }
279
280 binkp_atexit();
281
282 if (db->config.telnet_port)
283 telnet_atexit();
284 if (db->config.modem_port)
285 serial_atexit();
286 if (db->ipdb)
287 ipdb_close(&db->ipdb);
288 if (db->config.syslog_ip)
289 syslog_deinit();
290
291 db_close(db);
292 }
293
294 bool
295 handle_menu(long menu_id)
296 {
297 bool ret = false, quit;
298 size_t n;
299
300 switch (HiWord(menu_id)) {
301 case APPLE_MENU_ID:
302 switch (LoWord(menu_id)) {
303 case APPLE_MENU_ABOUT_ID:
304 about(PROGRAM_NAME);
305 break;
306 default: {
307 Str255 da;
308 GrafPtr save_port;
309
310 GetItem(apple_menu, LoWord(menu_id), &da);
311 GetPort(&save_port);
312 OpenDeskAcc(da);
313 SetPort(save_port);
314 break;
315 }
316 }
317 break;
318 case FILE_MENU_ID:
319 switch (LoWord(menu_id)) {
320 case FILE_MENU_QUIT_ID: {
321 int tnfocusables = nfocusables;
322 struct focusable **tfocusables;
323
324 ret = true;
325 quit = true;
326
327 if (nfocusables) {
328 /*
329 * nfocusables and focusables array will probably be
330 * modified as each focusable quits
331 */
332 tfocusables = xcalloc(sizeof(Ptr), tnfocusables);
333 if (tfocusables == NULL)
334 ExitToShell();
335
336 memcpy(tfocusables, focusables,
337 sizeof(Ptr) * tnfocusables);
338
339 for (n = 0; n < tnfocusables; n++) {
340 if (tfocusables[n] && tfocusables[n]->quit &&
341 !tfocusables[n]->quit(tfocusables[n])) {
342 quit = false;
343 break;
344 }
345 }
346
347 xfree(&tfocusables);
348 }
349 if (quit)
350 ExitToShell();
351 break;
352 }
353 }
354 break;
355 case EDIT_MENU_ID:
356 if (nfocusables && focusables[0]->visible && focusables[0]->menu)
357 ret = focusables[0]->menu(focusables[0], HiWord(menu_id),
358 LoWord(menu_id));
359 break;
360 case BBS_MENU_ID:
361 switch (LoWord(menu_id)) {
362 case BBS_MENU_OPEN_CONSOLE_ID:
363 console_init();
364 break;
365 case BBS_MENU_RELOAD_VIEWS_ID:
366 db_cache_views(db);
367 break;
368 }
369 ret = true;
370 break;
371 #ifdef MALLOC_DEBUG
372 case DEBUG_MENU_DUMP_ID:
373 switch (LoWord(menu_id)) {
374 case 1:
375 xalloc_print(logger_printf);
376 break;
377 }
378 ret = true;
379 break;
380 #endif
381 }
382
383 HiliteMenu(0);
384 return ret;
385 }
386
387 void
388 blanker_idle(void)
389 {
390 if (db->config.blanker_idle_seconds == 0)
391 return;
392
393 if (!blanker_on && !nsessions &&
394 (Time - blanker_last_blank > db->config.blanker_idle_seconds)) {
395 HideMenuBar();
396
397 blanker_win = NewWindow(0L, &screenBits.bounds, "\p", false,
398 plainDBox, (WindowPtr)-1L, true, 0);
399 if (!blanker_win)
400 panic("NewWindow failed");
401 ShowWindow(blanker_win);
402 SetPort(blanker_win);
403 HideCursor();
404 FillRect(&screenBits.bounds, black);
405 blanker_last_blank = Time;
406 blanker_on = true;
407 return;
408 }
409
410 if (blanker_on && (nsessions ||
411 (Time - blanker_last_blank >= db->config.blanker_runtime_seconds)))
412 blanker_unblank();
413 }
414
415 void
416 blanker_unblank(void)
417 {
418 unsigned long rt;
419
420 if (!blanker_on || !blanker_win)
421 return;
422
423 rt = Time - blanker_last_blank;
424
425 ShowCursor();
426 DisposeWindow(blanker_win);
427 blanker_win = NULL;
428 RestoreHiddenMenuBar();
429 blanker_last_blank = Time;
430 blanker_on = false;
431
432 /*
433 * This will call back to blanker_unblank, so it must be done after
434 * blanker_on = false
435 */
436 logger_printf("[blanker] Blanked screen for %ld second%s",
437 rt, rt == 1 ? "" : "s");
438 }
439
440 void
441 periodic_jobs(struct uthread *uthread, void *arg)
442 {
443 struct tm *date_tm;
444 size_t n;
445 short last_daily_job = -1;
446
447 for (;;) {
448 date_tm = localtime((time_t *)&Time);
449
450 if (last_daily_job != date_tm->tm_mday) {
451 if (nsessions != 0)
452 goto sleep;
453
454 if (date_tm->tm_year + 1900 < 2024) {
455 logger_printf("[db] Bogus system clock (year %d), not "
456 "pruning", date_tm->tm_year + 1900);
457 last_daily_job = date_tm->tm_mday;
458 goto sleep;
459 }
460
461 if (db->config.session_log_prune_days) {
462 session_prune_logs(db->config.session_log_prune_days);
463 uthread_yield();
464 }
465
466 if (db->config.mail_prune_days) {
467 mail_prune(db->config.mail_prune_days);
468 uthread_yield();
469 }
470
471 for (n = 0; n < db->nboards; n++) {
472 board_prune_old_posts(&db->boards[n]);
473 uthread_yield();
474 }
475
476 last_daily_job = date_tm->tm_mday;
477 }
478
479 if (binkp_ready && (nsessions == 0 || binkp_next_poll == 0)) {
480 /*
481 * Only do polling when no one is on, or we were just woken up
482 * from the sysop menu
483 */
484 if (Time > binkp_next_poll ||
485 (!binkp_last_poll_error && binkp_packets_in_outbox())) {
486 binkp_poll();
487 }
488 }
489
490 sleep:
491 uthread_msleep((unsigned long)1000 * 10);
492 }
493
494 periodic_job_thread = NULL;
495 }