AmendHub

Download

jcs

/

wallops

/

util.c

 

(View History)

jcs   util/focusable: Import newer versions from other projects Latest amendment: 39 on 2022-11-30

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