AmendHub

Download

jcs

/

subtext

/

util.c

 

(View History)

jcs   util: panic on xfree(NULL) and xfree(&NULL) Latest amendment: 252 on 2022-09-16

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