AmendHub

Download

jcs

/

amend

/

util.c

 

(View History)

jcs   *: Lots of little fixes and dead variable removal Latest amendment: 110 on 2023-02-06

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