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