AmendHub

Download

jcs

/

subtext

/

util.c

 

(View History)

jcs   util: Make xalloc tables dynamically allocated Latest amendment: 593 on 2024-02-16

1 /*
2 * Copyright (c) 2020-2022 joshua stein <jcs@jcs.org>
3 * Copyright (c) 1998, 2015 Todd C. Miller <millert@openbsd.org>
4 * Copyright (c) 1990, 1993
5 * The Regents of the University of California. All rights reserved.
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20 /* THINK C Project must have a header of "#include <MacHeaders>" */
21
22 #include <stdarg.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <GestaltEqu.h>
27 #include <Script.h>
28 #include <SetUpA4.h>
29 #include "util.h"
30
31 /* ALRT resources */
32 #define ASK_ALERT_ID 130
33
34 #define ERROR_STRING_SIZE 256
35 static char err_str[ERROR_STRING_SIZE];
36
37 /* basic DITL with an ok button (1), text area (2), and icon (3) */
38 #define ALERT_DITL_OK 1
39 #define ALERT_DITL_ICON 3
40 static const char alert_ditl[] = {
41 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4E,
42 0x00, 0xFA, 0x00, 0x64, 0x01, 0x34, 0x04, 0x02,
43 0x4F, 0x4B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0D,
44 0x00, 0x4E, 0x00, 0x41, 0x01, 0x36, 0x08, 0x02,
45 0x5E, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0D,
46 0x00, 0x17, 0x00, 0x2D, 0x00, 0x37, 0xA0, 0x02,
47 0x00, 0x01
48 };
49 static Handle alert_ditl_h = NULL;
50
51 /* ICN# to show for APPICON_ALERT */
52 #define APPICON_ICN_ID 128
53
54 /* DITL with a Yes button (1), No button (2), text (3), and icon (4) */
55 static const char ask_ditl[] = {
56 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46,
57 0x00, 0xE6, 0x00, 0x5A, 0x01, 0x20, 0x04, 0x03,
58 0x59, 0x65, 0x73, 0x21, 0x00, 0x00, 0x00, 0x00,
59 0x00, 0x46, 0x00, 0xA0, 0x00, 0x5A, 0x00, 0xDA,
60 0x04, 0x02, 0x4E, 0x6F, 0x00, 0x00, 0x00, 0x00,
61 0x00, 0x0A, 0x00, 0x32, 0x00, 0x41, 0x01, 0x22,
62 0x08, 0x02, 0x5E, 0x30, 0x00, 0x00, 0x00, 0x00,
63 0x00, 0x0A, 0x00, 0x0A, 0x00, 0x2A, 0x00, 0x2A,
64 0xA0, 0x02, 0x00, 0x01
65 };
66 static Handle ask_ditl_h = NULL;
67
68 /* DITL with just a text view */
69 static const char progress_ditl[] = {
70 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14,
71 0x00, 0x1E, 0x00, 0x32, 0x01, 0x3B, 0x08, 0x02,
72 0x5E, 0x30
73 };
74 static Handle progress_ditl_h = NULL;
75 static DialogPtr progress_dialog = NULL;
76
77 static TEHandle track_control_te = NULL;
78
79 enum {
80 STOP_ALERT,
81 CAUTION_ALERT,
82 NOTE_ALERT,
83 APPICON_ALERT
84 };
85
86 void vwarn(short alert_func, const char *format, va_list ap);
87
88 /*
89 * Util helper needed to be called at program startup, to pre-allocate
90 * some things that we can't do during errors.
91 */
92
93 void
94 util_init(void)
95 {
96 alert_ditl_h = xNewHandle(sizeof(alert_ditl));
97 if (alert_ditl_h == NULL)
98 ExitToShell();
99 HLock(alert_ditl_h);
100 memcpy(*alert_ditl_h, alert_ditl, sizeof(alert_ditl));
101 HUnlock(alert_ditl_h);
102 }
103
104 /*
105 * Memory functions
106 */
107
108 #define MUL_NO_OVERFLOW ((size_t)1 << (sizeof(size_t) * 4))
109
110 #ifdef MALLOC_DEBUG
111 /*
112 * malloc tracking, storing a stack trace of each xmalloc call and its
113 * size, which can be dumped with xalloc_print(). xfree also gains the
114 * ability to detect junk frees.
115 *
116 * Requires project options 'Always generate stack frames',
117 * 'Macsbug Names', and 'Long format' to be enabled.
118 */
119 #if !__option(force_frame) || !__option(long_macsbug_names)
120 #error
121 #endif
122
123 #define XALLOC_SYMBOL_SIZE 32
124 #define XALLOC_NSYMBOLS 64
125 #define XALLOC_MAX_STACK_DEPTH 10
126 static char *symbols = NULL;
127 struct xalloc {
128 char *stack[XALLOC_MAX_STACK_DEPTH];
129 unsigned long addr;
130 unsigned long size;
131 };
132 #define XALLOC_NXALLOCS 512
133 static struct xalloc *xallocs = NULL;
134 #endif
135
136 void *
137 xmalloc(size_t size)
138 {
139 void *ptr;
140 #ifdef MALLOC_DEBUG
141 static char cursym[XALLOC_SYMBOL_SIZE], *tsym, *sym;
142 struct xalloc *xa;
143 unsigned long _a6, ret;
144 unsigned char *code;
145 size_t n, cn, j;
146 short nframe;
147 #endif
148
149 if (size == 0)
150 panic("xmalloc: zero size");
151
152 ptr = NewPtr(size);
153 if (ptr == NULL)
154 return NULL;
155
156 #ifdef MALLOC_DEBUG
157 asm {
158 move.l a6,_a6
159 };
160
161 if (symbols == NULL) {
162 symbols = NewPtr(n = XALLOC_SYMBOL_SIZE * XALLOC_NSYMBOLS);
163 if (symbols == NULL)
164 panic("NewPtr failed for xalloc symbols");
165 memset(symbols, 0, n);
166 }
167 if (xallocs == NULL) {
168 xallocs = (struct xalloc *)NewPtr(n = (XALLOC_NXALLOCS *
169 sizeof(struct xalloc)));
170 if (xallocs == NULL)
171 panic("NewPtr failed for xallocs");
172 memset(xallocs, 0, n);
173 }
174
175 xa = NULL;
176 for (n = 0; n < XALLOC_NXALLOCS; n++) {
177 if (xallocs[n].addr == 0) {
178 xa = &xallocs[n];
179 break;
180 }
181 }
182 if (xa == NULL)
183 panic("out of xalloc space");
184
185 /*
186 * A6 register points to previous stack A6. Above A6 is our return
187 * address. Walk the code after the return address looking for an RTS
188 * instruction, then walk 3 bytes after that and we should have that
189 * function's symbol. Then jump to the previous A6 that our A6 points
190 * to and do it again to find the previous function symbol, repeating
191 * until we hit "main" or "uthread_begin".
192 */
193 for (nframe = 0; nframe < XALLOC_MAX_STACK_DEPTH; nframe++) {
194 ret = *(unsigned long *)(_a6 + 4);
195 code = (unsigned char *)ret;
196
197 cursym[0] = '\0';
198 for (; ; code += 2) {
199 if (code[0] == 0x4e && code[1] == 0x75) { /* RTS */
200 code += 3;
201 strlcpy(cursym, (char *)code, sizeof(cursym));
202 break;
203 }
204 }
205
206 if (!cursym[0])
207 break;
208
209 sym = NULL;
210 for (n = 0; n < XALLOC_NSYMBOLS; n++) {
211 tsym = symbols + (XALLOC_SYMBOL_SIZE * n);
212
213 if (tsym[0] == '\0')
214 break;
215 if (strcmp(tsym, cursym) == 0) {
216 sym = tsym;
217 break;
218 }
219 }
220 if (sym == NULL) {
221 for (n = 0; n < XALLOC_NSYMBOLS; n++) {
222 tsym = symbols + (XALLOC_SYMBOL_SIZE * n);
223 if (tsym[0] != '\0')
224 continue;
225
226 strlcpy(tsym, cursym, XALLOC_SYMBOL_SIZE);
227 sym = tsym;
228 break;
229 }
230 }
231 if (sym == NULL)
232 break;
233
234 xa->stack[nframe] = sym;
235
236 if (strcmp(sym, "main") == 0 || strcmp(sym, "uthread_begin") == 0)
237 break;
238
239 /* walk back up the chain */
240 _a6 = *(unsigned long *)_a6;
241 }
242
243 xa->addr = (unsigned long)ptr;
244 xa->size = size;
245 #endif
246
247 return ptr;
248 }
249
250 void
251 xfree(void *ptrptr)
252 {
253 unsigned long *addr = (unsigned long *)ptrptr;
254 void *ptr;
255 #ifdef MALLOC_DEBUG
256 size_t n;
257 #endif
258
259 if (ptrptr == NULL)
260 panic("xfree(NULL)");
261
262 ptr = (void *)*addr;
263 if (ptr == NULL)
264 panic("xfree(&NULL) likely a double-free");
265
266 #ifdef MALLOC_DEBUG
267 for (n = 0; n < XALLOC_NXALLOCS; n++) {
268 if (xallocs[n].addr == (unsigned long)ptr) {
269 memset(&xallocs[n], 0, sizeof(struct xalloc));
270 break;
271 }
272 }
273 if (n == XALLOC_NXALLOCS)
274 panic("xfree() without xalloc, possibly a junk free",
275 (unsigned long)ptr);
276 #endif
277
278 DisposePtr(ptr);
279 *addr = 0L;
280 }
281
282 void *
283 xmalloczero(size_t size)
284 {
285 void *ptr;
286
287 ptr = xmalloc(size);
288 if (ptr != NULL)
289 memset(ptr, 0, size);
290
291 return ptr;
292 }
293
294 void *
295 xcalloc(size_t nmemb, size_t size)
296 {
297 if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) &&
298 nmemb > 0 && SIZE_MAX / nmemb < size)
299 panic("xcalloc(%lu, %lu) overflow", nmemb, size);
300
301 return xmalloczero(nmemb * size);
302 }
303
304 void *
305 xrealloc(void *src, size_t size)
306 {
307 void *ptr, *tsrc;
308
309 ptr = xmalloc(size);
310 if (ptr != NULL && src != NULL) {
311 memcpy(ptr, src, size);
312 tsrc = src;
313 xfree(&tsrc);
314 }
315
316 return ptr;
317 }
318
319 void *
320 xreallocarray(void *optr, size_t nmemb, size_t size)
321 {
322 if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) &&
323 nmemb > 0 && SIZE_MAX / nmemb < size)
324 panic("xreallocarray(%lu, %lu) failed", nmemb, size);
325
326 return xrealloc(optr, size * nmemb);
327 }
328
329 char *
330 xstrdup(const char *str)
331 {
332 char *cp;
333 size_t len;
334
335 len = strlen(str);
336 cp = xmalloc(len + 1);
337 if (cp != NULL)
338 strlcpy(cp, str, len + 1);
339
340 return cp;
341 }
342
343 char *
344 xstrndup(const char *str, size_t maxlen)
345 {
346 char *copy;
347 const char *cp;
348 size_t len;
349
350 /* strnlen */
351 for (cp = str; maxlen != 0 && *cp != '\0'; cp++, maxlen--)
352 ;
353
354 len = (size_t)(cp - str);
355 copy = xmalloc(len + 1);
356 if (copy != NULL) {
357 (void)memcpy(copy, str, len);
358 copy[len] = '\0';
359 }
360
361 return copy;
362 }
363
364 #ifdef MALLOC_DEBUG
365 void
366 xalloc_print(size_t (*printer)(const char *, ...))
367 {
368 ssize_t n, j, len;
369 size_t total = 0, nallocs = 0;
370 char chain[128];
371
372 for (n = 0; n < XALLOC_NXALLOCS; n++) {
373 if (xallocs[n].addr == 0)
374 continue;
375
376 total += xallocs[n].size;
377 nallocs++;
378
379 chain[0] = '\0';
380 len = 0;
381 for (j = XALLOC_MAX_STACK_DEPTH - 1; j >= 0; j--) {
382 if (xallocs[n].stack[j] == 0)
383 continue;
384
385 if (chain[0] != '\0') {
386 if (len >= sizeof(chain) - 2)
387 break;
388 chain[len++] = ' ';
389 chain[len++] = ':';
390 chain[len++] = ' ';
391 chain[len++] = '\0';
392 }
393
394 len = strlcat(chain, xallocs[n].stack[j], sizeof(chain));
395 if (len >= sizeof(chain))
396 break;
397 }
398
399 printer("%ld @ %s", xallocs[n].size, chain);
400 }
401
402 printer("%ld bytes in %ld allocations in use", total, nallocs);
403 }
404 #endif
405
406 /* if var having used_size can't fit add, grow it by grow_amount */
407 bool
408 grow_to_fit(void *var, size_t *var_size, size_t used_size, size_t add,
409 size_t grow_amount)
410 {
411 char **p = var;
412 char *curp = *p;
413 char *newp;
414 size_t new_size;
415
416 if (used_size + add < *var_size)
417 return true;
418
419 new_size = *var_size;
420 while (new_size < used_size + add)
421 new_size += grow_amount;
422
423 newp = xrealloc(curp, new_size);
424 if (newp == NULL)
425 return false;
426
427 *var_size = new_size;
428 *p = newp;
429
430 return true;
431 }
432
433 /*
434 * String functions
435 */
436
437 short
438 getline(char *str, size_t len, char **ret)
439 {
440 short i;
441
442 for (i = 0; i < len; i++) {
443 if (str[i] == '\r' || i == len - 1) {
444 if (*ret == NULL) {
445 *ret = xmalloc(i + 1);
446 if (*ret == NULL)
447 return -1;
448 }
449 memcpy(*ret, str, i + 1);
450 (*ret)[i] = '\0';
451 return i + 1;
452 }
453 }
454
455 return 0;
456 }
457
458 const char *
459 ordinal(unsigned short n)
460 {
461 switch (n % 100) {
462 case 11:
463 case 12:
464 case 13:
465 return "th";
466 default:
467 switch (n % 10) {
468 case 1:
469 return "st";
470 case 2:
471 return "nd";
472 case 3:
473 return "rd";
474 default:
475 return "th";
476 }
477 }
478 }
479
480 size_t
481 rtrim(char *str, char *chars)
482 {
483 size_t len, rlen, n, j;
484
485 rlen = len = strlen(str);
486
487 for (n = len; n > 0; n--) {
488 for (j = 0; chars[j] != '\0'; j++) {
489 if (str[n - 1] == chars[j]) {
490 rlen--;
491 str[n - 1] = '\0';
492 goto next_in_str;
493 }
494 }
495
496 break;
497 next_in_str:
498 continue;
499 }
500
501 return rlen;
502 }
503
504 long
505 strpos_quoted(char *str, char c)
506 {
507 long pos;
508 unsigned char quot = 0;
509
510 for (pos = 0; str[pos] != '\0'; pos++) {
511 if (quot) {
512 if (str[pos] == '\\') {
513 pos++;
514 continue;
515 }
516 if (str[pos] == quot)
517 quot = 0;
518 continue;
519 } else {
520 if (str[pos] == '"') {
521 quot = str[pos];
522 continue;
523 }
524 if (str[pos] == c)
525 return pos;
526 }
527 }
528
529 return -1;
530 }
531
532 char *
533 OSTypeToString(OSType type)
534 {
535 static char ostype_s[5];
536
537 ostype_s[0] = (unsigned char)((type >> 24) & 0xff);
538 ostype_s[1] = (unsigned char)((type >> 16) & 0xff);
539 ostype_s[2] = (unsigned char)((type >> 8) & 0xff);
540 ostype_s[3] = (unsigned char)(type & 0xff);
541 ostype_s[4] = 0;
542
543 return ostype_s;
544 }
545
546 /*
547 * BSD err(3) and warn(3) functions
548 */
549
550 void
551 vwarn(short alert_func, const char *format, va_list ap)
552 {
553 Rect bounds;
554 short hit, itype;
555 WindowPtr win, dialog;
556 Handle ihandle;
557 Rect irect;
558
559 GetPort(&win);
560
561 vsnprintf(err_str, ERROR_STRING_SIZE, format, ap);
562
563 ParamText(CtoPstr(err_str), "\p", "\p", "\p");
564
565 center_in_screen(320, 110, false, &bounds);
566 dialog = NewDialog(nil, &bounds, "\p", false, dBoxProc,
567 (WindowPtr)-1L, false, 0, alert_ditl_h);
568
569 GetDItem(dialog, ALERT_DITL_ICON, &itype, &ihandle, &irect);
570 switch (alert_func) {
571 case CAUTION_ALERT:
572 ihandle = GetIcon(cautionIcon);
573 break;
574 case NOTE_ALERT:
575 ihandle = GetIcon(noteIcon);
576 break;
577 case APPICON_ALERT:
578 ihandle = GetResource('ICN#', APPICON_ICN_ID);
579 if (ihandle)
580 break;
581 default:
582 ihandle = GetIcon(stopIcon);
583 }
584 SetDItem(dialog, ALERT_DITL_ICON, itype, ihandle, &irect);
585
586 ShowWindow(dialog);
587
588 for (;;) {
589 ModalDialog(0, &hit);
590 if (hit == ok)
591 break;
592 }
593 ReleaseResource(ihandle);
594 DisposDialog(dialog);
595
596 /*
597 * Pre-allocate for the next one while we have memory. We can't use
598 * xNewHandle because we might already be alerting about an OOM
599 * condition.
600 */
601 DisposHandle(alert_ditl_h);
602 alert_ditl_h = NewHandle(sizeof(alert_ditl));
603 if (alert_ditl_h == NULL)
604 ExitToShell();
605 HLock(alert_ditl_h);
606 memcpy(*alert_ditl_h, alert_ditl, sizeof(alert_ditl));
607 HUnlock(alert_ditl_h);
608
609 SetPort(win);
610 }
611
612 void
613 panic(const char *format, ...)
614 {
615 va_list ap;
616
617 va_start(ap, format);
618 vwarn(STOP_ALERT, format, ap);
619 va_end(ap);
620
621 ExitToShell();
622 }
623
624 void
625 err(short ret, const char *format, ...)
626 {
627 va_list ap;
628
629 va_start(ap, format);
630 vwarn(STOP_ALERT, format, ap);
631 va_end(ap);
632
633 ExitToShell();
634 }
635
636 void
637 warn(const char *format, ...)
638 {
639 va_list ap;
640
641 va_start(ap, format);
642 vwarn(CAUTION_ALERT, format, ap);
643 va_end(ap);
644 }
645
646 void
647 warnx(const char *format, ...)
648 {
649 va_list ap;
650
651 va_start(ap, format);
652 vwarn(CAUTION_ALERT, format, ap);
653 va_end(ap);
654 }
655
656 void
657 note(const char *format, ...)
658 {
659 va_list ap;
660
661 va_start(ap, format);
662 vwarn(NOTE_ALERT, format, ap);
663 va_end(ap);
664 }
665
666 void
667 appicon_note(const char *format, ...)
668 {
669 va_list ap;
670
671 va_start(ap, format);
672 vwarn(APPICON_ALERT, format, ap);
673 va_end(ap);
674 }
675
676 short
677 ask(const char *format, ...)
678 {
679 Rect bounds;
680 short hit;
681 WindowPtr win, dialog;
682 va_list ap;
683
684 GetPort(&win);
685
686 va_start(ap, format);
687 vsnprintf(err_str, ERROR_STRING_SIZE, format, ap);
688 va_end(ap);
689
690 ParamText(CtoPstr(err_str), "\p", "\p", "\p");
691
692 ask_ditl_h = xNewHandle(sizeof(ask_ditl));
693 HLock(ask_ditl_h);
694 memcpy(*ask_ditl_h, ask_ditl, sizeof(ask_ditl));
695 HUnlock(ask_ditl_h);
696
697 center_in_screen(300, 100, false, &bounds);
698 dialog = NewDialog(nil, &bounds, "\p", false, dBoxProc,
699 (WindowPtr)-1L, false, 0, ask_ditl_h);
700
701 ShowWindow(dialog);
702 for (;;) {
703 ModalDialog(0, &hit);
704 if (hit == 1 || hit == 2)
705 break;
706 }
707 DisposDialog(dialog);
708 DisposHandle(ask_ditl_h);
709
710 SetPort(win);
711
712 return (hit == 1);
713 }
714
715 void
716 about(char *program_name)
717 {
718 VersRecHndl vers;
719 char vers_s[255];
720 char short_vers[255] = { 0 };
721 short vlen, n;
722
723 if ((vers = (VersRecHndl)GetResource('vers', 1))) {
724 /*
725 * vers "long version string" is a pascal string after the
726 * short version pascal string
727 */
728 HLock(vers);
729 vlen = (*vers)->shortVersion[0];
730 memcpy(short_vers, (*vers)->shortVersion + vlen + 1,
731 sizeof((*vers)->shortVersion) - vlen - 1);
732 PtoCstr(short_vers);
733 snprintf(vers_s, sizeof(vers_s), "%s %s", program_name,
734 short_vers);
735 for (n = 0; n < sizeof(vers_s); n++) {
736 if (vers_s[n] == '©') {
737 vers_s[n - 1] = '\r';
738 break;
739 }
740 }
741 ReleaseResource(vers);
742 appicon_note("%s", vers_s);
743 } else
744 warnx("Can't find version number!");
745 }
746
747 void
748 progress(char *format, ...)
749 {
750 static Str255 progress_s;
751 Handle thandle;
752 va_list argptr;
753 Rect bounds;
754 Rect trect;
755 short ttype;
756
757 if (format == NULL) {
758 if (progress_dialog != NULL) {
759 DisposDialog(progress_dialog);
760 DisposHandle(progress_ditl_h);
761 progress_dialog = NULL;
762 }
763 return;
764 }
765
766 va_start(argptr, format);
767 vsnprintf((char *)progress_s, 256, format, argptr);
768 va_end(argptr);
769 CtoPstr(progress_s);
770
771 if (progress_dialog == NULL) {
772 progress_ditl_h = xNewHandle(sizeof(progress_ditl));
773 HLock(progress_ditl_h);
774 memcpy(*progress_ditl_h, progress_ditl, sizeof(progress_ditl));
775 HUnlock(progress_ditl_h);
776
777 center_in_screen(330, 60, false, &bounds);
778 progress_dialog = NewDialog(nil, &bounds, "\p", false, dBoxProc,
779 (WindowPtr)-1L, false, 0, progress_ditl_h);
780 }
781
782 GetDItem(progress_dialog, 1, &ttype, &thandle, &trect);
783 SetIText(thandle, progress_s);
784 if (progress_s[0] > 30) {
785 trect.top -= 8;
786 trect.bottom += 16;
787 SetDItem(progress_dialog, 1, ttype, thandle, &trect);
788 }
789
790 ShowWindow(progress_dialog);
791 DrawDialog(progress_dialog);
792 }
793
794 void
795 window_rect(WindowPtr win, Rect *ret)
796 {
797 *ret = (*(((WindowPeek)win)->strucRgn))->rgnBBox;
798 ret->left += 1;
799 ret->right -= 2;
800 ret->top -= 1;
801 ret->bottom -= 1;
802 }
803
804 void
805 center_in_screen(short width, short height, bool titlebar, Rect *b)
806 {
807 b->left = ((screenBits.bounds.right - screenBits.bounds.left) / 2) -
808 (width / 2);
809 b->top = ((screenBits.bounds.bottom - screenBits.bounds.top) / 2) -
810 (height / 2) + (titlebar ? GetMBarHeight() : 0);
811 b->right = b->left + width;
812 b->bottom = b->top + height;
813 }
814
815 Point
816 centered_sfget_dialog(void)
817 {
818 Point p;
819 Rect r;
820
821 center_in_screen(364, 216, false, &r);
822 p.h = r.left;
823 p.v = r.top;
824
825 return p;
826 }
827
828 Point
829 centered_sfput_dialog(void)
830 {
831 Point p;
832 Rect r;
833
834 center_in_screen(320, 200, false, &r);
835 p.h = r.left;
836 p.v = r.top;
837
838 return p;
839 }
840
841 /*
842 * General Mac-specific non-GUI functions
843 */
844
845 static unsigned long _xorshift_state = 0;
846 unsigned long
847 xorshift32(void)
848 {
849 unsigned long x = _xorshift_state;
850 if (x == 0)
851 x = Ticks;
852 x ^= x << 13;
853 x ^= x >> 17;
854 x ^= x << 5;
855 return _xorshift_state = x;
856 }
857
858
859 /*
860 * Error checking wrappers for Mac toolkit functions
861 */
862
863 Handle
864 xNewHandle(size_t size)
865 {
866 Handle h;
867
868 if (size == 0)
869 panic("Zero xNewHandle size");
870
871 h = NewHandle(size);
872 if (h == NULL)
873 panic("Failed to NewHandle(%lu)", size);
874
875 return h;
876 }
877
878 Handle
879 xGetResource(ResType type, short id)
880 {
881 Handle h;
882
883 h = GetResource(type, id);
884 if (h == NULL)
885 panic("Failed to find resource %d", id);
886
887 return h;
888 }
889
890 StringHandle
891 xGetString(short id)
892 {
893 StringHandle h;
894
895 h = GetString(id);
896 if (h == NULL)
897 panic("Failed to find STR resource %d", id);
898
899 return h;
900 }
901
902 char *
903 xGetStringAsChar(short id)
904 {
905 StringHandle h;
906 char *out;
907 size_t l;
908
909 h = xGetString(id);
910 HLock(h);
911 l = (*h)[0];
912 out = xmalloc(l + 1);
913 if (out != NULL) {
914 memcpy((void *)out, (void *)(*h + 1), l);
915 out[l] = '\0';
916 }
917 ReleaseResource(h);
918
919 return out;
920 }
921
922 bool
923 xGetStringAsLong(short id, long *ret)
924 {
925 char *c;
926
927 c = xGetStringAsChar(id);
928 if (c == NULL)
929 return false;
930 *ret = atol(c);
931 xfree(&c);
932 return true;
933 }
934
935 bool
936 xSetHandleSize(Handle h, Size s)
937 {
938 SetHandleSize(h, s);
939 if (MemError()) {
940 warn("Failed to SetHandleSize to %ld", s);
941 return false;
942 }
943 return true;
944 }
945
946 /*
947 * Filesystem utilities
948 */
949
950 short
951 getpath(short vRefNum, Str255 fileName, Str255 ret, bool include_file)
952 {
953 WDPBRec wdir;
954 HVolumeParam wvol;
955 DirInfo wcinfo;
956 char *name = NULL;
957 size_t retlen = 0, len;
958 char *tmpret = NULL, *tmp = NULL;
959 char *lastcolon;
960
961 ret[0] = 0;
962
963 if (strchr((char *)fileName + 1, ':') != NULL) {
964 /* already a full path */
965 memcpy(ret, fileName, 256);
966 if (!include_file) {
967 PtoCstr(ret);
968 lastcolon = strrchr((char *)ret, ':');
969 lastcolon[0] = '\0';
970 CtoPstr(ret);
971 }
972 return 0;
973 }
974
975 name = xmalloc(FILENAME_MAX);
976 if (name == NULL)
977 return 1;
978
979 wdir.ioVRefNum = wdir.ioWDVRefNum = vRefNum;
980 wdir.ioWDIndex = 0;
981 wdir.ioWDProcID = 0;
982 wdir.ioNamePtr = (StringPtr)name;
983 if (PBGetWDInfo(&wdir, 0) != noErr) {
984 warn("Failed looking up directory");
985 xfree(&name);
986 return 1;
987 }
988
989 wvol.ioNamePtr = (StringPtr)name;
990 wvol.ioVRefNum = vRefNum;
991 wvol.ioVolIndex = 0;
992
993 if (PBHGetVInfoSync((HParmBlkPtr)&wvol) != noErr) {
994 warn("Failed getting volume info");
995 xfree(&name);
996 return 1;
997 }
998
999 if (wvol.ioVSigWord != 0x4244) {
1000 warn("Unknown filesystem type 0x%x", wvol.ioVSigWord);
1001 xfree(&name);
1002 return 1;
1003 }
1004
1005 wcinfo.ioVRefNum = vRefNum;
1006 wcinfo.ioNamePtr = (StringPtr)name;
1007 wcinfo.ioFDirIndex = -1;
1008 wcinfo.ioDrParID = wdir.ioWDDirID;
1009 wcinfo.ioDrDirID = wdir.ioWDDirID;
1010
1011 tmp = xmalloc(FILENAME_MAX);
1012 if (tmp == NULL) {
1013 xfree(&name);
1014 return 1;
1015 }
1016 tmpret = xmalloc(FILENAME_MAX);
1017 if (tmp == NULL) {
1018 xfree(&tmp);
1019 xfree(&name);
1020 return 1;
1021 }
1022
1023 /* go backwards, prepending each folder's parent */
1024 while (wcinfo.ioDrParID != 1) {
1025 wcinfo.ioDrDirID = wcinfo.ioDrParID; /* .. */
1026
1027 if (PBGetCatInfo((CInfoPBPtr)&wcinfo, 0) != noErr)
1028 break;
1029
1030 len = name[0];
1031 PtoCstr(name);
1032 if (retlen == 0) {
1033 retlen = len;
1034 strlcpy(tmpret, (char *)name, FILENAME_MAX);
1035 } else {
1036 strlcpy(tmp, tmpret, FILENAME_MAX);
1037 snprintf(tmpret, FILENAME_MAX, "%s:%s", name, tmp);
1038 }
1039 }
1040
1041 if (include_file) {
1042 /* append the original path */
1043 memcpy(name, fileName, FILENAME_MAX);
1044 PtoCstr(name);
1045 if (retlen == 0)
1046 strlcpy(tmpret, name, FILENAME_MAX);
1047 else {
1048 strlcat(tmpret, ":", FILENAME_MAX);
1049 strlcat(tmpret, name, FILENAME_MAX);
1050 }
1051 } else if (retlen == 0) {
1052 ret[0] = 0;
1053 xfree(&tmp);
1054 xfree(&tmpret);
1055 xfree(&name);
1056 return 0;
1057 }
1058
1059 CtoPstr(tmpret);
1060 memcpy(ret, tmpret, FILENAME_MAX);
1061 xfree(&tmp);
1062 xfree(&tmpret);
1063 xfree(&name);
1064
1065 return 0;
1066 }
1067
1068 short
1069 stat(char *path, struct stat *sb)
1070 {
1071 char *ppath;
1072 short ret;
1073
1074 ppath = xstrdup(path);
1075 if (ppath == NULL)
1076 return -1;
1077 CtoPstr(ppath);
1078 ret = FStat((unsigned char *)ppath, sb);
1079 xfree(&ppath);
1080
1081 return ret;
1082 }
1083
1084 short
1085 FStat(Str255 path, struct stat *sb)
1086 {
1087 CInfoPBRec catblock = { 0 };
1088 short ret;
1089
1090 catblock.hFileInfo.ioNamePtr = path;
1091 ret = PBGetCatInfo(&catblock, 0);
1092 if (ret != noErr)
1093 return -1;
1094
1095 /* data fork logical length, ignoring resource fork logical length */
1096 sb->st_size = catblock.hFileInfo.ioFlLgLen;
1097 sb->st_ctime = catblock.hFileInfo.ioFlCrDat;
1098 sb->st_mtime = catblock.hFileInfo.ioFlMdDat;
1099 sb->st_flags = catblock.hFileInfo.ioFlAttrib;
1100
1101 return 0;
1102 }
1103
1104 bool
1105 FIsDir(Str255 path)
1106 {
1107 struct stat st;
1108
1109 if (FStat(path, &st) != 0)
1110 return false;
1111
1112 /* bit 4 is set in ioFlAttrib if the item is a directory */
1113 if (st.st_flags & (1 << 4))
1114 return true;
1115
1116 return false;
1117 }
1118
1119 OSErr
1120 copy_file(Str255 source, Str255 dest, bool overwrite)
1121 {
1122 FInfo fi;
1123 IOParam pb;
1124 short error, source_ref, dest_ref;
1125
1126 /* copy data fork */
1127
1128 error = GetFInfo(source, 0, &fi);
1129 if (error)
1130 return error;
1131
1132 error = Create(dest, 0, fi.fdCreator, fi.fdType);
1133 if (error == dupFNErr && overwrite) {
1134 error = FSDelete(dest, 0);
1135 if (error)
1136 return error;
1137 error = Create(dest, 0, fi.fdCreator, fi.fdType);
1138 }
1139 if (error)
1140 return error;
1141
1142 memset(&pb, 0, sizeof(pb));
1143 pb.ioNamePtr = source;
1144 pb.ioPermssn = fsRdPerm;
1145 error = PBOpen(&pb, false);
1146 if (error)
1147 return error;
1148 source_ref = pb.ioRefNum;
1149
1150 error = FSOpen(dest, 0, &dest_ref);
1151 if (error) {
1152 FSClose(source_ref);
1153 return error;
1154 }
1155
1156 error = copy_file_contents(source_ref, dest_ref);
1157
1158 FSClose(source_ref);
1159 FSClose(dest_ref);
1160
1161 if (error)
1162 return error;
1163
1164 /*
1165 * Copy resource fork, open source as shared read/write in case it's
1166 * an open resource file.
1167 */
1168 source_ref = OpenRFPerm(source, 0, fsRdWrShPerm);
1169
1170 if (source_ref == -1 && ResError() == eofErr) {
1171 /* no resource fork */
1172 FSClose(source_ref);
1173 FSClose(dest_ref);
1174 return 0;
1175 }
1176
1177 if (source_ref == -1)
1178 return ResError();
1179
1180 CreateResFile(dest);
1181 if (ResError()) {
1182 FSClose(source_ref);
1183 return ResError();
1184 }
1185 error = OpenRF(dest, 0, &dest_ref);
1186 if (error) {
1187 FSClose(source_ref);
1188 return error;
1189 }
1190
1191 error = copy_file_contents(source_ref, dest_ref);
1192
1193 FSClose(source_ref);
1194 FSClose(dest_ref);
1195
1196 return error;
1197 }
1198
1199 OSErr
1200 copy_file_contents(short source_ref, short dest_ref)
1201 {
1202 char *buf;
1203 short error;
1204 long source_size, count;
1205
1206 GetEOF(source_ref, &source_size);
1207 error = SetFPos(source_ref, fsFromStart, 0);
1208 if (error)
1209 return error;
1210 error = SetEOF(dest_ref, source_size);
1211 if (error)
1212 return error;
1213 error = SetFPos(dest_ref, fsFromStart, 0);
1214 if (error)
1215 return error;
1216
1217 buf = xmalloc(1024);
1218 if (buf == NULL)
1219 return -1;
1220
1221 while (source_size > 0) {
1222 count = 1024;
1223 if (count > source_size)
1224 count = source_size;
1225 error = FSRead(source_ref, &count, buf);
1226 if (error && error != eofErr)
1227 break;
1228 source_size -= count;
1229 error = FSWrite(dest_ref, &count, buf);
1230 if (error && error != eofErr)
1231 break;
1232 }
1233
1234 xfree(&buf);
1235
1236 if (error && error != eofErr)
1237 return error;
1238
1239 return 0;
1240 }
1241
1242 /* read a \r-terminated line or the final non-line bytes of an open file */
1243 OSErr
1244 FSReadLine(short frefnum, char *buf, size_t buflen)
1245 {
1246 char tbuf;
1247 size_t pos, fsize, rlen = 1, total_read = 0;
1248 short error;
1249
1250 GetFPos(frefnum, &pos);
1251 GetEOF(frefnum, &fsize);
1252
1253 for (; pos <= fsize; pos++) {
1254 if (total_read > buflen)
1255 return -1;
1256
1257 error = FSRead(frefnum, &rlen, &tbuf);
1258 if (error)
1259 return -1;
1260
1261 if (tbuf == '\r')
1262 return total_read;
1263
1264 buf[total_read++] = tbuf;
1265 }
1266
1267 /* nothing found until the end of the file */
1268 return total_read;
1269 }
1270
1271
1272 /*
1273 * Gestalt functions
1274 */
1275 char *
1276 gestalt_machine_type(void)
1277 {
1278 short error;
1279 long resp;
1280
1281 error = Gestalt(gestaltMachineType, &resp);
1282 if (error)
1283 return NULL;
1284 switch (resp) {
1285 case gestaltClassic:
1286 return "Macintosh 128K";
1287 case gestaltMacXL:
1288 return "Macintosh XL";
1289 case gestaltMac512KE:
1290 return "Macintosh 512Ke";
1291 case gestaltMacPlus:
1292 return "Macintosh Plus";
1293 case gestaltMacSE:
1294 return "Macintosh SE";
1295 case gestaltMacII:
1296 return "Macintosh II";
1297 case gestaltMacIIx:
1298 return "Macintosh IIx";
1299 case gestaltMacIIcx:
1300 return "Macintosh IIcx";
1301 case gestaltMacSE030:
1302 return "Macintosh SE/30";
1303 case gestaltPortable:
1304 return "Macintosh Portable";
1305 case gestaltMacIIci:
1306 return "Macintosh IIci";
1307 case gestaltMacIIfx:
1308 return "Macintosh IIfx";
1309 case gestaltMacClassic:
1310 return "Macintosh Classic";
1311 case gestaltMacIIsi:
1312 return "Macintosh IIsi";
1313 case gestaltMacLC:
1314 return "Macintosh LC";
1315 }
1316
1317 return NULL;
1318 }
1319
1320 char *
1321 get_version(bool long_version)
1322 {
1323 static char vers_s[256] = { 0 };
1324 char *v;
1325 VersRecHndl vers;
1326 short len;
1327
1328 vers = (VersRecHndl)GetResource('vers', 1);
1329 if (!vers)
1330 return "?.?";
1331
1332 HLock(vers);
1333 v = (char *)&(*vers)->shortVersion;
1334 len = v[0];
1335 if (long_version) {
1336 v += len + 1;
1337 len = v[0];
1338 }
1339 memcpy(vers_s, v, len + 1);
1340 ReleaseResource(vers);
1341
1342 PtoCstr(vers_s);
1343 return vers_s;
1344 }
1345
1346 /*
1347 * General Mac-specific GUI functions
1348 */
1349
1350 short
1351 FontHeight(short font_id, short size)
1352 {
1353 FMInput f_in;
1354 FMOutput *f_out;
1355
1356 if (font_id == -1)
1357 font_id = thePort->txFont;
1358 if (size == -1)
1359 size = thePort->txSize;
1360
1361 f_in.family = font_id;
1362 f_in.size = size;
1363 f_in.face = 0;
1364 f_in.needBits = false;
1365 f_in.device = 0;
1366 f_in.numer.h = f_in.numer.v = 1;
1367 f_in.denom.h = f_in.denom.v = 1;
1368
1369 f_out = FMSwapFont(&f_in);
1370
1371 return (((f_out->leading + f_out->ascent + f_out->descent) *
1372 f_out->numer.v) / f_out->denom.v);
1373 }
1374
1375 void
1376 DrawGrowIconOnly(WindowPtr win)
1377 {
1378 Rect r;
1379 RgnHandle tmp;
1380
1381 GetClip(tmp = NewRgn());
1382 if (tmp == NULL) {
1383 warn("NewRgn() failed");
1384 return;
1385 }
1386 r = win->portRect;
1387 r.top = r.bottom - SCROLLBAR_WIDTH;
1388 r.left = r.right - SCROLLBAR_WIDTH + 1;
1389 ClipRect(&r);
1390 DrawGrowIcon(win);
1391 SetClip(tmp);
1392 DisposeRgn(tmp);
1393 }
1394
1395 /* assumes a fixed-width style */
1396 short
1397 TEGetWidth(short off, TEHandle te)
1398 {
1399 TextStyle style;
1400 short fheight, fwidth, ascent, old_font, old_size;
1401
1402 TEGetStyle(off, &style, &fheight, &ascent, te);
1403
1404 old_font = thePort->txFace;
1405 old_size = thePort->txSize;
1406 thePort->txFace = style.tsFont;
1407 thePort->txSize = style.tsSize;
1408 fwidth = CharWidth('m');
1409 thePort->txFace = old_font;
1410 thePort->txSize = old_size;
1411
1412 return fwidth;
1413 }
1414
1415 #define ceildiv(a,b) ((a / b) + (!!(a % b)))
1416
1417 void
1418 UpdateScrollbarForTE(GrafPtr win, ControlHandle control, TEHandle te, bool reset)
1419 {
1420 size_t vlines, telines;
1421 TERec *ter;
1422 RgnHandle rgn;
1423 short fheight, fwidth, max, val, per_line, horiz, max_chars, n;
1424
1425 HLock(control);
1426 HLock(te);
1427 ter = *te;
1428
1429 horiz = (((*control)->contrlRect.bottom - (*control)->contrlRect.top) <
1430 ((*control)->contrlRect.right - (*control)->contrlRect.left));
1431
1432 if (horiz) {
1433 fwidth = TEGetWidth(0, te);
1434 per_line = ((ter->viewRect.right - ter->viewRect.left) /
1435 fwidth) - 1;
1436 for (max_chars = 0, n = 1; n < ter->nLines; n++) {
1437 if (ter->lineStarts[n] - ter->lineStarts[n - 1] > max_chars)
1438 max_chars = ter->lineStarts[n] - ter->lineStarts[n - 1];
1439 }
1440
1441 if (max_chars <= per_line)
1442 /* don't enable the scrollbar */
1443 max = 1;
1444 else
1445 max = max_chars - per_line + 1;
1446
1447 if (reset) {
1448 val = 1;
1449 TESetSelect(0, 0, te);
1450 } else {
1451 val = (ter->viewRect.left - ter->destRect.left);
1452
1453 if (val < 0)
1454 val = 1;
1455 else {
1456 val = ceildiv(val, fwidth) + 1;
1457 if (val > max)
1458 max = val;
1459 }
1460 }
1461 } else {
1462 fheight = TEGetHeight(0, 0, te);
1463 vlines = (ter->viewRect.bottom - ter->viewRect.top) / fheight;
1464 telines = ter->nLines;
1465 /* telines is inaccurate if the last line doesn't have any chars */
1466 //if (telines >= vlines)
1467 // telines++;
1468 max = telines - vlines + 1;
1469 if (max < 1)
1470 max = 1;
1471
1472 if (reset) {
1473 val = 1;
1474 TESetSelect(0, 0, te);
1475 } else {
1476 val = (ter->viewRect.top - ter->destRect.top);
1477 if (val < 0)
1478 val = 1;
1479 else {
1480 val = ceildiv(val, fheight) + 1;
1481 if (val > max)
1482 max = val;
1483 }
1484 }
1485 }
1486
1487 /*
1488 * Avoid SetCtlMax because it will redraw and then SetCtlValue will
1489 * redraw again, which can cause a jump if we're trying to keep the
1490 * scrollbar position in the same place (like at the bottom).
1491 */
1492 (*control)->contrlMax = max;
1493 SetCtlValue(control, val);
1494
1495 rgn = NewRgn();
1496 RectRgn(rgn, &(*control)->contrlRect);
1497 UpdtControl(win, rgn);
1498 CloseRgn(rgn);
1499
1500 HUnlock(te);
1501 HUnlock(control);
1502 }
1503
1504 void
1505 SetTrackControlTE(TEHandle te)
1506 {
1507 track_control_te = te;
1508 }
1509
1510 pascal void
1511 TrackMouseDownInControl(ControlHandle control, short part)
1512 {
1513 TERec *ter;
1514 short page, val, adj, fheight, fwidth, horiz, per_line;
1515
1516 if (track_control_te == NULL)
1517 panic("TrackMouseDownInControl without SetTrackControlTE");
1518
1519 HLock(track_control_te);
1520 ter = *track_control_te;
1521
1522 horiz = (((*control)->contrlRect.bottom - (*control)->contrlRect.top) <
1523 ((*control)->contrlRect.right - (*control)->contrlRect.left));
1524
1525 if (horiz) {
1526 fwidth = TEGetWidth(0, track_control_te);
1527 per_line = ((ter->viewRect.right - ter->viewRect.left) /
1528 fwidth) - 1;
1529 page = ceildiv(GetCtlMax(control) + per_line,
1530 ((per_line * 75) / 100)) + 1;
1531 } else {
1532 /* keep 1 line of context between pages */
1533 fheight = TEGetHeight(0, 0, track_control_te);
1534 page = ((ter->viewRect.bottom - ter->viewRect.top) / fheight) - 1;
1535 }
1536
1537 adj = 0;
1538 switch (part) {
1539 case inUpButton:
1540 adj = -1;
1541 break;
1542 case inPageUp:
1543 adj = -page;
1544 break;
1545 case inDownButton:
1546 adj = 1;
1547 break;
1548 case inPageDown:
1549 adj = page;
1550 break;
1551 }
1552
1553 val = GetCtlValue(control);
1554 if (val + adj < GetCtlMin(control))
1555 adj = -(val - GetCtlMin(control));
1556 if (val + adj > GetCtlMax(control))
1557 adj = (GetCtlMax(control) - val);
1558 if (adj == 0)
1559 return;
1560
1561 if (horiz)
1562 TEScroll(-adj * fwidth, 0, track_control_te);
1563 else
1564 TEScroll(0, -adj * fheight, track_control_te);
1565
1566 SetCtlValue(control, val + adj);
1567
1568 HUnlock(track_control_te);
1569 }
1570
1571 pascal bool
1572 ModalDialogFilter(DialogPtr dlg, EventRecord *event, short *hit)
1573 {
1574 WindowPtr event_win;
1575 short event_in;
1576 char key;
1577
1578 switch (event->what) {
1579 case mouseDown:
1580 event_in = FindWindow(event->where, &event_win);
1581
1582 switch (event_in) {
1583 case inGoAway:
1584 if (TrackGoAway(dlg, event->where)) {
1585 *hit = -1;
1586 return true;
1587 }
1588 }
1589 break;
1590 case keyDown:
1591 key = event->message & charCodeMask;
1592
1593 if (event->modifiers & cmdKey) {
1594 switch (key) {
1595 case 'x':
1596 ZeroScrap();
1597 DlgCut(dlg);
1598 break;
1599 case 'c':
1600 ZeroScrap();
1601 DlgCopy(dlg);
1602 break;
1603 case 'v':
1604 if (TEFromScrap() == noErr)
1605 DlgPaste(dlg);
1606 break;
1607 }
1608 event->what = nullEvent;
1609 return false;
1610 } else if (key == 13 || key == 3) {
1611 /* OK button */
1612 *hit = OK;
1613 return true;
1614 }
1615
1616 break;
1617 }
1618
1619 return false;
1620 }
1621
1622 static short _password_dialog_ditl_id = -1;
1623 static char *_password_dialog_storage = NULL;
1624 static size_t _password_dialog_storage_len = 0;
1625
1626 void
1627 PasswordDialogFieldFilterSetup(short ditl_id, char *storage, size_t len)
1628 {
1629 _password_dialog_ditl_id = ditl_id;
1630 _password_dialog_storage = storage;
1631 _password_dialog_storage_len = len;
1632
1633 memset(_password_dialog_storage, 0, len);
1634 }
1635
1636 pascal bool
1637 PasswordDialogFieldFilter(DialogPtr dlg, EventRecord *event, short *hit)
1638 {
1639 DialogPeek dlgp;
1640 short sel_start, sel_end;
1641 char key;
1642
1643 dlgp = (DialogPeek)dlg;
1644 if (dlgp->editField == _password_dialog_ditl_id - 1) {
1645 sel_start = (*(dlgp->textH))->selStart;
1646 sel_end = (*(dlgp->textH))->selEnd;
1647
1648 switch (event->what) {
1649 case keyDown:
1650 case autoKey:
1651 key = event->message & charCodeMask;
1652 if (event->modifiers & cmdKey) {
1653 /* TODO: implement DlgPaste for cmd+v? */
1654 event->what = nullEvent;
1655 return false;
1656 }
1657
1658 if (key == 8) {
1659 /* backspace */
1660 if (sel_start == sel_end && sel_start > 0)
1661 memmove(_password_dialog_storage + sel_start - 1,
1662 _password_dialog_storage + sel_start,
1663 _password_dialog_storage_len - sel_start - 1);
1664 else if (sel_start != sel_end)
1665 memmove(_password_dialog_storage + sel_start,
1666 _password_dialog_storage + sel_end,
1667 _password_dialog_storage_len - sel_end - 1);
1668 } else if (sel_start >= _password_dialog_storage_len) {
1669 event->what = nullEvent;
1670 return false;
1671 } else if (key >= ' ' && key <= '~') {
1672 if (sel_start != sel_end)
1673 /* delete selection before making space for new char */
1674 memmove(_password_dialog_storage + sel_start,
1675 _password_dialog_storage + sel_end,
1676 _password_dialog_storage_len - sel_end - 1);
1677 memmove(_password_dialog_storage + sel_start + 1,
1678 _password_dialog_storage + sel_start,
1679 _password_dialog_storage_len - sel_start - 1);
1680 _password_dialog_storage[sel_start] = key;
1681 event->message = '•';
1682 }
1683 _password_dialog_storage[(*(dlgp->textH))->teLength + 1] = '\0';
1684 sel_start = 0;
1685 break;
1686 }
1687 }
1688
1689 return ModalDialogFilter(dlg, event, hit);
1690 }
1691
1692 void
1693 PasswordDialogFieldFinish(void)
1694 {
1695 _password_dialog_ditl_id = -1;
1696 _password_dialog_storage = NULL;
1697 _password_dialog_storage_len = 0;
1698 }
1699
1700 /* (*(some_te))->caretHook = NullCaretHook; */
1701 pascal void
1702 NullCaretHook(void)
1703 {
1704 asm {
1705 move.l (a7)+,d0
1706 rts
1707 }
1708 }
1709
1710 static bool menu_bar_hidden = false;
1711 static short old_mbar_height;
1712 static Rect mbar_rect;
1713 static RgnHandle mbar_save_region;
1714
1715 void
1716 HideMenuBar(void)
1717 {
1718 RgnHandle mbar_region;
1719 WindowPeek win;
1720
1721 old_mbar_height = GetMBarHeight();
1722 SetRect(&mbar_rect, screenBits.bounds.left, screenBits.bounds.top,
1723 screenBits.bounds.right, screenBits.bounds.top + old_mbar_height);
1724
1725 mbar_save_region = NewRgn();
1726 mbar_region = NewRgn();
1727
1728 MBarHeight = 0;
1729 CopyRgn(GetGrayRgn(), mbar_save_region);
1730 RectRgn(mbar_region, &mbar_rect);
1731 UnionRgn(GetGrayRgn(), mbar_region, GetGrayRgn());
1732
1733 win = (WindowPeek)FrontWindow();
1734 PaintOne(win, mbar_region);
1735 PaintBehind(win, mbar_region);
1736 CalcVis(win);
1737 CalcVisBehind(win, mbar_region);
1738 DisposeRgn(mbar_region);
1739
1740 menu_bar_hidden = true;
1741 }
1742
1743 void
1744 RestoreHiddenMenuBar(void)
1745 {
1746 WindowPeek win;
1747
1748 if (!menu_bar_hidden)
1749 return;
1750
1751 CopyRgn(mbar_save_region, GetGrayRgn());
1752 MBarHeight = old_mbar_height;
1753
1754 RectRgn(mbar_save_region, &mbar_rect);
1755 win = (WindowPeek)FrontWindow();
1756 CalcVis(win);
1757 CalcVisBehind(win, mbar_save_region);
1758 DisposeRgn(mbar_save_region);
1759
1760 menu_bar_hidden = false;
1761
1762 HiliteMenu(0);
1763 DrawMenuBar();
1764 }
1765
1766 /* C Extensions */
1767
1768 /*
1769 * Appends src to string dst of size dsize (unlike strncat, dsize is the
1770 * full size of dst, not space left). At most dsize-1 characters
1771 * will be copied. Always NUL terminates (unless dsize <= strlen(dst)).
1772 * Returns strlen(src) + MIN(dsize, strlen(initial dst)).
1773 * If retval >= dsize, truncation occurred.
1774 */
1775 size_t
1776 strlcat(char *dst, const char *src, size_t dsize)
1777 {
1778 const char *odst = dst;
1779 const char *osrc = src;
1780 size_t n = dsize;
1781 size_t dlen;
1782
1783 /* Find the end of dst and adjust bytes left but don't go past end. */
1784 while (n-- != 0 && *dst != '\0')
1785 dst++;
1786 dlen = dst - odst;
1787 n = dsize - dlen;
1788
1789 if (n-- == 0)
1790 return(dlen + strlen(src));
1791 while (*src != '\0') {
1792 if (n != 0) {
1793 *dst++ = *src;
1794 n--;
1795 }
1796 src++;
1797 }
1798 *dst = '\0';
1799
1800 return(dlen + (src - osrc)); /* count does not include NUL */
1801 }
1802
1803 /*
1804 * Copy string src to buffer dst of size dsize. At most dsize-1
1805 * chars will be copied. Always NUL terminates (unless dsize == 0).
1806 * Returns strlen(src); if retval >= dsize, truncation occurred.
1807 */
1808 size_t
1809 strlcpy(char *dst, const char *src, size_t dsize)
1810 {
1811 const char *osrc = src;
1812 size_t nleft = dsize;
1813
1814 /* Copy as many bytes as will fit. */
1815 if (nleft != 0) {
1816 while (--nleft != 0) {
1817 if ((*dst++ = *src++) == '\0')
1818 break;
1819 }
1820 }
1821
1822 /* Not enough room in dst, add NUL and traverse rest of src. */
1823 if (nleft == 0) {
1824 if (dsize != 0)
1825 *dst = '\0'; /* NUL-terminate dst */
1826 while (*src++)
1827 ;
1828 }
1829
1830 return(src - osrc - 1); /* count does not include NUL */
1831 }
1832
1833 char *
1834 strndup(const char *str, size_t maxlen)
1835 {
1836 char *copy;
1837 const char *cp;
1838 size_t len;
1839
1840 /* strnlen */
1841 for (cp = str; maxlen != 0 && *cp != '\0'; cp++, maxlen--)
1842 ;
1843
1844 len = (size_t)(cp - str);
1845 copy = malloc(len + 1);
1846 if (copy != NULL) {
1847 (void)memcpy(copy, str, len);
1848 copy[len] = '\0';
1849 }
1850
1851 return copy;
1852 }
1853
1854 /*
1855 * Get next token from string *stringp, where tokens are possibly-empty
1856 * strings separated by characters from delim.
1857 *
1858 * Writes NULs into the string at *stringp to end tokens.
1859 * delim need not remain constant from call to call.
1860 * On return, *stringp points past the last NUL written (if there might
1861 * be further tokens), or is NULL (if there are definitely no more tokens).
1862 *
1863 * If *stringp is NULL, strsep returns NULL.
1864 */
1865 char *
1866 strsep(char **stringp, const char *delim)
1867 {
1868 char *s;
1869 const char *spanp;
1870 int c, sc;
1871 char *tok;
1872
1873 if ((s = *stringp) == NULL)
1874 return (NULL);
1875 for (tok = s;;) {
1876 c = *s++;
1877 spanp = delim;
1878 do {
1879 if ((sc = *spanp++) == c) {
1880 if (c == 0)
1881 s = NULL;
1882 else
1883 s[-1] = 0;
1884 *stringp = s;
1885 return (tok);
1886 }
1887 } while (sc != 0);
1888 }
1889 return (NULL);
1890 }
1891
1892 static struct format {
1893 unsigned leftJustify : 1;
1894 unsigned forceSign : 1;
1895 unsigned altForm : 1;
1896 unsigned zeroPad : 1;
1897 unsigned havePrecision : 1;
1898 unsigned hSize : 1;
1899 unsigned lSize : 1;
1900 unsigned LSize : 1;
1901 char sign;
1902 char exponent;
1903 int fieldWidth;
1904 int precision;
1905 } default_format;
1906
1907 short bounded_vfprintf(FILE *fp, const char *fmt, va_list arg);
1908 static int nullio(FILE *fp, int i);
1909
1910 short
1911 bounded_vfprintf(FILE *fp, const char *fmt, va_list arg)
1912 {
1913 register int c, i, nwritten = 0;
1914 register unsigned long n;
1915 register char *s;
1916 #define VFPRINTF_BUFLEN 512
1917 static char buf[VFPRINTF_BUFLEN], *digits, *t;
1918 struct format F;
1919
1920 for (c = *fmt; c; c = *++fmt) {
1921 if (c != '%')
1922 goto copy1;
1923 F = default_format;
1924
1925 /* decode flags */
1926
1927 for (;;) {
1928 c = *++fmt;
1929 if (c == '-')
1930 F.leftJustify = TRUE;
1931 else if (c == '+')
1932 F.forceSign = TRUE;
1933 else if (c == ' ')
1934 F.sign = ' ';
1935 else if (c == '#')
1936 F.altForm = TRUE;
1937 else if (c == '0')
1938 F.zeroPad = TRUE;
1939 else
1940 break;
1941 }
1942
1943 /* decode field width */
1944
1945 if (c == '*') {
1946 if ((F.fieldWidth = va_arg(arg, int)) < 0) {
1947 F.leftJustify = TRUE;
1948 F.fieldWidth = -F.fieldWidth;
1949 }
1950 c = *++fmt;
1951 }
1952 else {
1953 for (; c >= '0' && c <= '9'; c = *++fmt)
1954 F.fieldWidth = (10 * F.fieldWidth) + (c - '0');
1955 }
1956
1957 /* decode precision */
1958
1959 if (c == '.') {
1960 if ((c = *++fmt) == '*') {
1961 F.precision = va_arg(arg, int);
1962 c = *++fmt;
1963 }
1964 else {
1965 for (; c >= '0' && c <= '9'; c = *++fmt)
1966 F.precision = (10 * F.precision) + (c - '0');
1967 }
1968 if (F.precision >= 0)
1969 F.havePrecision = TRUE;
1970 }
1971
1972 /* perform appropriate conversion */
1973
1974 s = &buf[VFPRINTF_BUFLEN];
1975 if (F.leftJustify)
1976 F.zeroPad = FALSE;
1977 conv: switch (c) {
1978
1979 /* 'h' size modifier */
1980
1981 case 'h':
1982 F.hSize = TRUE;
1983 c = *++fmt;
1984 goto conv;
1985
1986 /* 'l' size modifier */
1987
1988 case 'l':
1989 F.lSize = TRUE;
1990 c = *++fmt;
1991 goto conv;
1992
1993 /* 'L' size modifier */
1994
1995 case 'L':
1996 F.LSize = TRUE;
1997 c = *++fmt;
1998 goto conv;
1999
2000 /* decimal (signed) */
2001
2002 case 'd':
2003 case 'i':
2004 if (F.lSize)
2005 n = va_arg(arg, long);
2006 else
2007 n = va_arg(arg, int);
2008 if (F.hSize)
2009 n = (short) n;
2010 if ((long) n < 0) {
2011 n = -n;
2012 F.sign = '-';
2013 }
2014 else if (F.forceSign)
2015 F.sign = '+';
2016 goto decimal;
2017
2018 /* decimal (unsigned) */
2019
2020 case 'u':
2021 if (F.lSize)
2022 n = va_arg(arg, unsigned long);
2023 else
2024 n = va_arg(arg, unsigned int);
2025 if (F.hSize)
2026 n = (unsigned short) n;
2027 F.sign = 0;
2028 goto decimal;
2029
2030 /* decimal (common code) */
2031
2032 decimal:
2033 if (!F.havePrecision) {
2034 if (F.zeroPad) {
2035 F.precision = F.fieldWidth;
2036 if (F.sign)
2037 --F.precision;
2038 }
2039 if (F.precision < 1)
2040 F.precision = 1;
2041 }
2042 for (i = 0; n; n /= 10, i++)
2043 *--s = n % 10 + '0';
2044 for (; i < F.precision; i++)
2045 *--s = '0';
2046 if (F.sign) {
2047 *--s = F.sign;
2048 i++;
2049 }
2050 break;
2051
2052 /* octal (unsigned) */
2053
2054 case 'o':
2055 if (F.lSize)
2056 n = va_arg(arg, unsigned long);
2057 else
2058 n = va_arg(arg, unsigned int);
2059 if (F.hSize)
2060 n = (unsigned short) n;
2061 if (!F.havePrecision) {
2062 if (F.zeroPad)
2063 F.precision = F.fieldWidth;
2064 if (F.precision < 1)
2065 F.precision = 1;
2066 }
2067 for (i = 0; n; n /= 8, i++)
2068 *--s = n % 8 + '0';
2069 if (F.altForm && i && *s != '0') {
2070 *--s = '0';
2071 i++;
2072 }
2073 for (; i < F.precision; i++)
2074 *--s = '0';
2075 break;
2076
2077 /* hexadecimal (unsigned) */
2078
2079 case 'p':
2080 F.havePrecision = F.lSize = TRUE;
2081 F.precision = 8;
2082 /* ... */
2083 case 'X':
2084 digits = "0123456789ABCDEF";
2085 goto hexadecimal;
2086 case 'x':
2087 digits = "0123456789abcdef";
2088 /* ... */
2089 hexadecimal:
2090 if (F.lSize)
2091 n = va_arg(arg, unsigned long);
2092 else
2093 n = va_arg(arg, unsigned int);
2094 if (F.hSize)
2095 n = (unsigned short) n;
2096 if (!F.havePrecision) {
2097 if (F.zeroPad) {
2098 F.precision = F.fieldWidth;
2099 if (F.altForm)
2100 F.precision -= 2;
2101 }
2102 if (F.precision < 1)
2103 F.precision = 1;
2104 }
2105 for (i = 0; n; n /= 16, i++)
2106 *--s = digits[n % 16];
2107 for (; i < F.precision; i++)
2108 *--s = '0';
2109 if (F.altForm) {
2110 *--s = c;
2111 *--s = '0';
2112 i += 2;
2113 }
2114 break;
2115
2116 /* character */
2117
2118 case 'c':
2119 *--s = va_arg(arg, int);
2120 i = 1;
2121 break;
2122
2123 /* string */
2124
2125 case 's':
2126 s = va_arg(arg, char *);
2127 if (F.altForm) {
2128 i = (unsigned char) *s++;
2129 if (F.havePrecision && i > F.precision)
2130 i = F.precision;
2131 }
2132 else {
2133 if (!F.havePrecision)
2134 i = strlen(s);
2135 else if ((t = memchr(s, '\0', F.precision)) != NULL)
2136 i = t - s;
2137 else
2138 i = F.precision;
2139 }
2140 break;
2141
2142 /* store # bytes written so far */
2143
2144 case 'n':
2145 s = va_arg(arg, void *);
2146 if (F.hSize)
2147 * (short *) s = nwritten;
2148 else if (F.lSize)
2149 * (long *) s = nwritten;
2150 else
2151 * (int *) s = nwritten;
2152 continue;
2153
2154 /* oops - unknown conversion, abort */
2155
2156 default:
2157 if (c != '%')
2158 goto done;
2159 copy1:
2160 putc(c, fp); /* disregard EOF */
2161 ++nwritten;
2162 continue;
2163 }
2164
2165 /* pad on the left */
2166
2167 if (i < F.fieldWidth && !F.leftJustify) {
2168 do {
2169 putc(' ', fp); /* disregard EOF */
2170 ++nwritten;
2171 } while (i < --F.fieldWidth);
2172 }
2173
2174 /* write the converted result */
2175
2176 fwrite(s, 1, i, fp); /* disregard EOF */
2177 nwritten += i;
2178
2179 /* pad on the right */
2180
2181 for (; i < F.fieldWidth; i++) {
2182 putc(' ', fp); /* disregard EOF */
2183 ++nwritten;
2184 }
2185 }
2186
2187 /* all done! */
2188
2189 done:
2190 return(nwritten);
2191 }
2192
2193 short
2194 snprintf(char *s, size_t size, const char *fmt, ...)
2195 {
2196 return(vsnprintf(s, size, fmt, __va(fmt)));
2197 }
2198
2199 static int
2200 nullio(FILE *fp, int i)
2201 {
2202 return(EOF);
2203 }
2204
2205 short
2206 vsnprintf(char *s, size_t size, const char *fmt, void *p)
2207 {
2208 FILE f;
2209 int n;
2210 unsigned char zb;
2211
2212 memset(&f, 0, sizeof(f));
2213 f.refnum = -1;
2214 f.ptr = (unsigned char *)s;
2215 f.cnt = size;
2216 f.proc = nullio;
2217 f.dirty = 1;
2218
2219 if (size == 0)
2220 zb = s[0];
2221
2222 if ((n = bounded_vfprintf(&f, fmt, p)) >= 0) {
2223 if (n < size)
2224 s[n] = 0;
2225 else
2226 s[size - 1] = 0;
2227 }
2228
2229 if (size == 0)
2230 s[0] = zb;
2231
2232 return(n);
2233 }