AmendHub

Download

jcs

/

wifi_da

/

util.c

 

(View History)

jcs   util: Add some DA-specific tweaks Latest amendment: 7 on 2023-10-20

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