Download
cyberslak
/lightsout
/util.c
(View History)
cyberslak Properly check for Thread Manager before using it | Latest amendment: 25 on 2025-04-21 |
1 | // SPDX-License-Identifier: MIT |
2 | |
3 | #define _UTIL_IMPL_ |
4 | #include "util.h" |
5 | #include <stdio.h> |
6 | #include <stdarg.h> |
7 | #include <string.h> |
8 | #include <stdlib.h> |
9 | |
10 | #define kMessageAlert 128 |
11 | |
12 | bool have_thread_manager() |
13 | { |
14 | OSErr err; |
15 | static long resp = -1; // cache response |
16 | |
17 | if (resp == -1) { |
18 | err = Gestalt(gestaltThreadMgrAttr, &resp); |
19 | if (err < 0) { |
20 | if (err != gestaltUndefSelectorErr) |
21 | warn("Gestalt failed: %d", err); |
22 | resp = 0; |
23 | } |
24 | } |
25 | |
26 | return (resp & (1 << gestaltThreadMgrPresent)); |
27 | } |
28 | |
29 | void win_center(WindowPtr win) |
30 | { |
31 | short barHeight = GetMBarHeight() * 2; // menu + title bar |
32 | short screenWidth = rect_width(&qd.screenBits.bounds); |
33 | short screenHeight = rect_height(&qd.screenBits.bounds) - barHeight; |
34 | short winWidth = rect_width(&win->portRect); |
35 | short winHeight = rect_height(&win->portRect); |
36 | |
37 | MoveWindow(win, (screenWidth - winWidth) / 2, barHeight + (screenHeight - winHeight) / 2, false); |
38 | } |
39 | |
40 | void* xmalloc(size_t sz) |
41 | { |
42 | void* p = malloc(sz); |
43 | if (!p) |
44 | die("malloc failed"); |
45 | return p; |
46 | } |
47 | |
48 | void* xrealloc(void* ptr, size_t newlen) |
49 | { |
50 | void* p = realloc(ptr, newlen); |
51 | if (!p) |
52 | die("realloc failed"); |
53 | return p; |
54 | } |
55 | |
56 | void die(const char* fmt, ...) |
57 | { |
58 | va_list arg; |
59 | char str[255]; |
60 | va_start(arg, fmt); |
61 | |
62 | lo_vsnprintf(str, 255, fmt, arg); |
63 | |
64 | va_end(arg); |
65 | |
66 | ParamText(c2pstr(str), nil, nil, nil); |
67 | StopAlert(kMessageAlert, nil); |
68 | ExitToShell(); |
69 | } |
70 | |
71 | void warn(const char* fmt, ...) |
72 | { |
73 | va_list arg; |
74 | char str[255]; |
75 | va_start(arg, fmt); |
76 | |
77 | lo_vsnprintf(str, 255, fmt, arg); |
78 | |
79 | va_end(arg); |
80 | |
81 | ParamText(c2pstr(str), nil, nil, nil); |
82 | CautionAlert(kMessageAlert, nil); |
83 | } |
84 | |
85 | void info(const char* fmt, ...) |
86 | { |
87 | va_list arg; |
88 | char str[255]; |
89 | va_start(arg, fmt); |
90 | |
91 | lo_vsnprintf(str, 255, fmt, arg); |
92 | |
93 | va_end(arg); |
94 | |
95 | ParamText(c2pstr(str), nil, nil, nil); |
96 | NoteAlert(kMessageAlert, nil); |
97 | } |
98 | |
99 | |
100 | void toolbox_init(void) |
101 | { |
102 | InitGraf(&qd.thePort); |
103 | InitFonts(); |
104 | FlushEvents(everyEvent, 0); |
105 | InitWindows(); |
106 | InitMenus(); |
107 | TEInit(); |
108 | InitDialogs(nil); |
109 | InitCursor(); |
110 | } |
111 | |
112 | void memory_init(void) |
113 | { |
114 | short i; |
115 | Ptr applLimit; |
116 | |
117 | applLimit = GetApplLimit(); |
118 | SetApplLimit(applLimit - 0x8000); |
119 | |
120 | MaxApplZone(); |
121 | for (i = 0; i < 5; ++i) |
122 | MoreMasters(); |
123 | } |
124 | |
125 | size_t lo_strlcpy(char* dest, const char* src, size_t len) |
126 | { |
127 | size_t srclen = strlen(src); |
128 | size_t copylen; |
129 | |
130 | if (len == 0) |
131 | return srclen; |
132 | |
133 | len--; // null terminator |
134 | |
135 | copylen = (srclen > len) ? len : srclen; |
136 | memcpy(dest, src, copylen); |
137 | dest[copylen] = 0; |
138 | return srclen; |
139 | } |
140 | |
141 | char* lo_strdup(const char* s) |
142 | { |
143 | size_t sz; |
144 | char *s2; |
145 | if (!s) |
146 | return NULL; |
147 | |
148 | sz = strlen(s); |
149 | s2 = xmalloc(sz + 1); |
150 | memcpy(s2, s, sz + 1); |
151 | return s2; |
152 | } |
153 | |
154 | // bounded *printf implementation |
155 | // taken from jcs' wallops, modified to |
156 | // take a generic output descriptor |
157 | // instead of a (mocked) FILE object. |
158 | |
159 | static struct format { |
160 | unsigned leftJustify : 1; |
161 | unsigned forceSign : 1; |
162 | unsigned altForm : 1; |
163 | unsigned zeroPad : 1; |
164 | unsigned havePrecision : 1; |
165 | unsigned hSize : 1; |
166 | unsigned lSize : 1; |
167 | unsigned LSize : 1; |
168 | char sign; |
169 | char exponent; |
170 | short fieldWidth; |
171 | short precision; |
172 | } default_format; |
173 | |
174 | /* Generic output object; can be used for FILE* |
175 | * or character array output as needed. |
176 | */ |
177 | typedef struct |
178 | { |
179 | void (*putch)(char, void*); |
180 | void (*write)(char*, size_t, void*); |
181 | void *data; |
182 | size_t maxlen; |
183 | } output_descriptor; |
184 | |
185 | short lo_vfprintf(output_descriptor *fp, const char *fmt, va_list arg); |
186 | |
187 | short |
188 | lo_vfprintf(output_descriptor *fp, const char *fmt, va_list arg) |
189 | { |
190 | register int c, i, j, nwritten = 0; |
191 | register unsigned long n; |
192 | long double x; |
193 | register char *s; |
194 | #define VFPRINTF_BUFLEN 512 |
195 | static char buf[VFPRINTF_BUFLEN], *digits, *t; |
196 | struct format F; |
197 | |
198 | for (c = *fmt; c; c = *++fmt) { |
199 | if (c != '%') |
200 | goto copy1; |
201 | F = default_format; |
202 | |
203 | /* decode flags */ |
204 | |
205 | for (;;) { |
206 | c = *++fmt; |
207 | if (c == '-') |
208 | F.leftJustify = TRUE; |
209 | else if (c == '+') |
210 | F.forceSign = TRUE; |
211 | else if (c == ' ') |
212 | F.sign = ' '; |
213 | else if (c == '#') |
214 | F.altForm = TRUE; |
215 | else if (c == '0') |
216 | F.zeroPad = TRUE; |
217 | else |
218 | break; |
219 | } |
220 | |
221 | /* decode field width */ |
222 | |
223 | if (c == '*') { |
224 | if ((F.fieldWidth = va_arg(arg, int)) < 0) { |
225 | F.leftJustify = TRUE; |
226 | F.fieldWidth = -F.fieldWidth; |
227 | } |
228 | c = *++fmt; |
229 | } |
230 | else { |
231 | for (; c >= '0' && c <= '9'; c = *++fmt) |
232 | F.fieldWidth = (10 * F.fieldWidth) + (c - '0'); |
233 | } |
234 | |
235 | /* decode precision */ |
236 | |
237 | if (c == '.') { |
238 | if ((c = *++fmt) == '*') { |
239 | F.precision = va_arg(arg, int); |
240 | c = *++fmt; |
241 | } |
242 | else { |
243 | for (; c >= '0' && c <= '9'; c = *++fmt) |
244 | F.precision = (10 * F.precision) + (c - '0'); |
245 | } |
246 | if (F.precision >= 0) |
247 | F.havePrecision = TRUE; |
248 | } |
249 | |
250 | /* perform appropriate conversion */ |
251 | |
252 | s = &buf[VFPRINTF_BUFLEN]; |
253 | if (F.leftJustify) |
254 | F.zeroPad = FALSE; |
255 | conv: switch (c) { |
256 | |
257 | /* 'h' size modifier */ |
258 | |
259 | case 'h': |
260 | F.hSize = TRUE; |
261 | c = *++fmt; |
262 | goto conv; |
263 | |
264 | /* 'l' size modifier */ |
265 | |
266 | case 'l': |
267 | F.lSize = TRUE; |
268 | c = *++fmt; |
269 | goto conv; |
270 | |
271 | /* 'L' size modifier */ |
272 | |
273 | case 'L': |
274 | F.LSize = TRUE; |
275 | c = *++fmt; |
276 | goto conv; |
277 | |
278 | /* decimal (signed) */ |
279 | |
280 | case 'd': |
281 | case 'i': |
282 | if (F.lSize) |
283 | n = va_arg(arg, long); |
284 | else |
285 | n = va_arg(arg, int); |
286 | if (F.hSize) |
287 | n = (short) n; |
288 | if ((long) n < 0) { |
289 | n = -n; |
290 | F.sign = '-'; |
291 | } |
292 | else if (F.forceSign) |
293 | F.sign = '+'; |
294 | goto decimal; |
295 | |
296 | /* decimal (unsigned) */ |
297 | |
298 | case 'u': |
299 | if (F.lSize) |
300 | n = va_arg(arg, unsigned long); |
301 | else |
302 | n = va_arg(arg, unsigned int); |
303 | if (F.hSize) |
304 | n = (unsigned short) n; |
305 | F.sign = 0; |
306 | goto decimal; |
307 | |
308 | /* decimal (common code) */ |
309 | |
310 | decimal: |
311 | if (!F.havePrecision) { |
312 | if (F.zeroPad) { |
313 | F.precision = F.fieldWidth; |
314 | if (F.sign) |
315 | --F.precision; |
316 | } |
317 | if (F.precision < 1) |
318 | F.precision = 1; |
319 | } |
320 | for (i = 0; n; n /= 10, i++) |
321 | *--s = n % 10 + '0'; |
322 | for (; i < F.precision; i++) |
323 | *--s = '0'; |
324 | if (F.sign) { |
325 | *--s = F.sign; |
326 | i++; |
327 | } |
328 | break; |
329 | |
330 | /* octal (unsigned) */ |
331 | |
332 | case 'o': |
333 | if (F.lSize) |
334 | n = va_arg(arg, unsigned long); |
335 | else |
336 | n = va_arg(arg, unsigned int); |
337 | if (F.hSize) |
338 | n = (unsigned short) n; |
339 | if (!F.havePrecision) { |
340 | if (F.zeroPad) |
341 | F.precision = F.fieldWidth; |
342 | if (F.precision < 1) |
343 | F.precision = 1; |
344 | } |
345 | for (i = 0; n; n /= 8, i++) |
346 | *--s = n % 8 + '0'; |
347 | if (F.altForm && i && *s != '0') { |
348 | *--s = '0'; |
349 | i++; |
350 | } |
351 | for (; i < F.precision; i++) |
352 | *--s = '0'; |
353 | break; |
354 | |
355 | /* hexadecimal (unsigned) */ |
356 | |
357 | case 'p': |
358 | F.havePrecision = F.lSize = TRUE; |
359 | F.precision = 8; |
360 | /* ... */ |
361 | case 'X': |
362 | digits = "0123456789ABCDEF"; |
363 | goto hexadecimal; |
364 | case 'x': |
365 | digits = "0123456789abcdef"; |
366 | /* ... */ |
367 | hexadecimal: |
368 | if (F.lSize) |
369 | n = va_arg(arg, unsigned long); |
370 | else |
371 | n = va_arg(arg, unsigned int); |
372 | if (F.hSize) |
373 | n = (unsigned short) n; |
374 | if (!F.havePrecision) { |
375 | if (F.zeroPad) { |
376 | F.precision = F.fieldWidth; |
377 | if (F.altForm) |
378 | F.precision -= 2; |
379 | } |
380 | if (F.precision < 1) |
381 | F.precision = 1; |
382 | } |
383 | for (i = 0; n; n /= 16, i++) |
384 | *--s = digits[n % 16]; |
385 | for (; i < F.precision; i++) |
386 | *--s = '0'; |
387 | if (F.altForm) { |
388 | *--s = c; |
389 | *--s = '0'; |
390 | i += 2; |
391 | } |
392 | break; |
393 | |
394 | /* character */ |
395 | |
396 | case 'c': |
397 | *--s = va_arg(arg, int); |
398 | i = 1; |
399 | break; |
400 | |
401 | /* string */ |
402 | |
403 | case 's': |
404 | s = va_arg(arg, char *); |
405 | if (F.altForm) { |
406 | i = (unsigned char) *s++; |
407 | if (F.havePrecision && i > F.precision) |
408 | i = F.precision; |
409 | } |
410 | else { |
411 | if (!F.havePrecision) |
412 | i = strlen(s); |
413 | else if (t = memchr(s, '\0', F.precision)) |
414 | i = t - s; |
415 | else |
416 | i = F.precision; |
417 | } |
418 | break; |
419 | |
420 | /* store # bytes written so far */ |
421 | |
422 | case 'n': |
423 | s = va_arg(arg, void *); |
424 | if (F.hSize) |
425 | * (short *) s = nwritten; |
426 | else if (F.lSize) |
427 | * (long *) s = nwritten; |
428 | else |
429 | * (int *) s = nwritten; |
430 | continue; |
431 | |
432 | /* oops - unknown conversion, abort */ |
433 | |
434 | default: |
435 | if (c != '%') |
436 | goto done; |
437 | copy1: |
438 | fp->putch(c, fp); /* disregard EOF */ |
439 | ++nwritten; |
440 | continue; |
441 | } |
442 | |
443 | /* pad on the left */ |
444 | |
445 | if (i < F.fieldWidth && !F.leftJustify) { |
446 | do { |
447 | fp->putch(' ', fp); /* disregard EOF */ |
448 | ++nwritten; |
449 | } while (i < --F.fieldWidth); |
450 | } |
451 | |
452 | /* write the converted result */ |
453 | |
454 | fp->write(s, i, fp); /* disregard EOF */ |
455 | nwritten += i; |
456 | |
457 | /* pad on the right */ |
458 | |
459 | for (; i < F.fieldWidth; i++) { |
460 | fp->putch(' ', fp); /* disregard EOF */ |
461 | ++nwritten; |
462 | } |
463 | } |
464 | |
465 | /* all done! */ |
466 | |
467 | done: |
468 | return(nwritten); |
469 | } |
470 | |
471 | short |
472 | lo_snprintf(char *s, size_t size, const char *fmt, ...) |
473 | { |
474 | va_list l; |
475 | short res; |
476 | |
477 | va_start(l, fmt); |
478 | res = lo_vsnprintf(s, size, fmt, l); |
479 | va_end(l); |
480 | return res; |
481 | } |
482 | |
483 | void str_putch(char c, void* p); |
484 | void str_write(char* s, size_t len, void* p); |
485 | |
486 | void str_putch(char c, void* p) |
487 | { |
488 | output_descriptor* f = p; |
489 | char* d = f->data; |
490 | if (f->maxlen) |
491 | { |
492 | *((char*)f->data) = c; |
493 | f->data = (char*)f->data + 1; |
494 | f->maxlen--; |
495 | } |
496 | } |
497 | |
498 | void str_write(char* s, size_t len, void* p) |
499 | { |
500 | output_descriptor* f = p; |
501 | size_t to_write = MIN(f->maxlen, len); |
502 | memcpy(f->data, s, to_write); |
503 | f->data = (char*)f->data + to_write; |
504 | f->maxlen -= to_write; |
505 | } |
506 | |
507 | short |
508 | lo_vsnprintf(char *s, size_t size, const char *fmt, void *p) |
509 | { |
510 | output_descriptor f; |
511 | int n; |
512 | int zb; |
513 | |
514 | memset(&f, 0, sizeof(f)); |
515 | f.putch = str_putch; |
516 | f.write = str_write; |
517 | f.data = s; |
518 | f.maxlen = size; |
519 | |
520 | if (size == 0) |
521 | zb = s[0]; |
522 | |
523 | if ((n = lo_vfprintf(&f, fmt, p)) >= 0) { |
524 | if (n < size) |
525 | s[n] = 0; |
526 | else if (size > 0) |
527 | s[size - 1] = 0; |
528 | } |
529 | |
530 | if (size == 0) |
531 | s[0] = zb; |
532 | |
533 | return(n); |
534 | } |