AmendHub

Download

jcs

/

subtext

/

util.c

 

(View History)

jcs   util: Minor bugfixes Latest amendment: 640 on 2026-09-22

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